---
title: 'Migration Guide | Cypress Documentation'
description: 'A guide to help you migrate to the latest version of Cypress.'
sidebar_label: 'Migration Guide'
---

<ProductHeading product="app" />

# Migration Guide

## Migrating to Cypress 15.0

This guide details the code changes needed to migrate to Cypress
version 15.
[See the full changelog for version v15.0](/app/references/changelog#15-0-0).

### Node.js 20, 22 and 24+ support

Cypress requires [Node.js](https://nodejs.org/en) in order to install the Cypress binary and the supported versions are now Node.js 20, 22, 24 and above.
Node.js versions 18 and 23 are no longer supported.
[See Node's release schedule](https://github.com/nodejs/Release).

### cy.exec code property renamed

The `code` property on [`cy.exec()`](/api/commands/exec) has been renamed to `exitCode`.

<Badge type="danger">Before</Badge>{' '}

```javascript
cy.exec('rake db:seed').its('code').should('eq', 0)
```

<Badge type="success">After</Badge>

```javascript
cy.exec('rake db:seed').its('exitCode').should('eq', 0)
```

### Unsupported Linux Distributions

Prebuilt binaries for Linux are no longer compatible with Linux distributions based on glibc `<2.31`.
This support is in line with Node.js's support for Linux in 20+.

If you're using a Linux distribution based on glibc `<2.31`, you'll need to
update your system to a newer version to install Cypress 15+.
To display which version of glibc your Linux system is running, execute `ldd --version`.

### Webpack `4` is no longer supported

Cypress is no longer supporting Webpack `4` as it is no longer maintained by the core Webpack team and Webpack `5` has been available since Q4 2020. This includes dropping Webpack `4` support for:

- `@cypress/webpack-dev-server` for component testing. This use case is most common and will require an update to Webpack `5`.
  - `@cypress/webpack-dev-server` also no longer supports [Webpack Dev Server v4](https://github.com/webpack/webpack-dev-server/tree/v4.15.2). We shipped [Webpack Dev Server v5](https://github.com/webpack/webpack-dev-server/tree/v5.2.1) as the default in Cypress 14 with `webpack-dev-server@4` being an option.
- `@cypress/webpack-preprocessor` for end-to-end testing. Cypress, by default, uses the [Webpack Batteries Included Preprocessor](https://github.com/cypress-io/cypress/blob/@cypress/webpack-batteries-included-preprocessor-v3.0.7/npm/webpack-batteries-included-preprocessor/README.md) to process your files for end-to-end testing, which has used Webpack 5 since Cypress 13. Unless you are already using `@cypress/webpack-preprocessor` as a standalone package, this change likely does not apply.

#### To continue using Webpack `4`

##### Component Testing

If you haven't been able to migrate away from Webpack `4` or Webpack Dev Server `4` and still need to be able to run your component tests with Webpack `4` or Webpack Dev Server `4`, you can install the following packages independently:

```sh
npm install --save-dev @cypress/webpack-dev-server@4
```

and configure the dev server within your `cypress.config.js` or `cypress.config.ts` file:

```js
import { devServer } from '@cypress/webpack-dev-server'
import { defineConfig } from 'cypress'

export default defineConfig({
  component: {
    devServer(devServerConfig) {
      return devServer({
        ...devServerConfig,
        framework: 'react',
        webpackConfig: require('./webpack.config.js'),
      })
    },
  },
})
```

Note that this package version is deprecated and no longer supported by Cypress and is intended as a workaround until you can migrate to Webpack `5`. More information on how to configure the dev server `v4 `can be found in the [Cypress Webpack Dev Server documentation](https://github.com/cypress-io/cypress/blob/@cypress/webpack-dev-server-v4.0.2/npm/webpack-dev-server/README.md) and [Custom Dev Server documentation](/app/component-testing/component-framework-configuration#Custom-Dev-Server).

##### End-to-End Testing

If you haven't been able to migrate away from Webpack `4`, need custom end-to-end spec file preprocessing, are already using `@cypress/webpack-preprocessor` as a standalone package, and still need to be able to run your end-to-end tests with Webpack `4`, you can install the following package independently:

```sh
npm install --save-dev @cypress/webpack-preprocessor@6
```

and configure the preprocessor within your `cypress.config.js` or `cypress.config.ts` file:

```js
import { defineConfig } from 'cypress'
import webpackPreprocessor from '@cypress/webpack-preprocessor'

export default defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      on('file:preprocessor', webpackPreprocessor())
    },
  },
})
```

As stated earlier, this is likely unnecessary unless you are already using `@cypress/webpack-preprocessor` as a standalone package. Cypress by default uses the [Webpack Batteries Included Preprocessor](https://github.com/cypress-io/cypress/blob/@cypress/webpack-batteries-included-preprocessor-v3.0.7/npm/webpack-batteries-included-preprocessor/README.md) to process your spec files for end-to-end testing.

Note that this package version is deprecated and no longer supported by Cypress and is intended as a workaround until you can migrate to Webpack `5`. More information on how to configure the preprocessor can be found in the [Preprocessors API documentation](/api/node-events/preprocessors-api#Usage) and [Webpack Preprocessor documentation](https://github.com/cypress-io/cypress/blob/@cypress/webpack-preprocessor-v6.0.4/npm/webpack-preprocessor/README.md).

### `@cypress/webpack-batteries-included-preprocessor` no longer shims all built-ins provided by `webpack` v4

The default file preprocessor, `@cypress/webpack-batteries-included-preprocessor`, no longer shims all built-ins that were previously provided by webpack v4. This is mainly to reduce security vulnerabilities and bundle size within the end-to-end file preprocessor.

However, `@cypress/webpack-batteries-included-preprocessor` still ships with _some_ built-ins, such as `buffer`, `path`, `process`, `os`, and `stream`. If other built-ins are required, install `@cypress/webpack-batteries-included-preprocessor` independently and follow the webpack documentation described in [webpack's resolve.fallback](https://webpack.js.org/configuration/resolve/#resolvefallback) to configure the built-ins you need.

For example, the following code shows how to provide the `querystring` built-in to the preprocessor:

```javascript
const webpackPreprocessor = require('@cypress/webpack-batteries-included-preprocessor')

function getWebpackOptions() {
  const options = webpackPreprocessor.getFullWebpackOptions()

  // add built-ins as needed
  // NOTE: for this example, querystring-es3 needs to be installed as a dependency
  options.resolve.fallback.querystring = require.resolve('querystring-es3')
  return options
}

module.exports = (on) => {
  on(
    'file:preprocessor',
    webpackPreprocessor({
      // if using typescript, you will need to set the typescript option to true
      typescript: true,
      webpackOptions: getWebpackOptions(),
    })
  )
}
```

### Angular `17` CT no longer supported

With [LTS end](https://angular.dev/reference/releases#actively-supported-versions) for Angular 17, the minimum required Angular version for component testing is now `18.0.0`.

#### To continue using Angular below 18.0.0

If you haven't been able to migrate away from an older Angular version and still need that test harness, it can be installed independently via the [`@cypress/angular`](https://www.npmjs.com/package/@cypress/angular) `3.x.x` package from `npm`.

Note that this test harness version is deprecated and no longer supported by Cypress. This version is intended to serve as a temporary workaround to migrate your project to Angular v18.0.0+.

```sh
npm install --save-dev @cypress/angular@3
```

Inside your support file (ex: `./cypress/support/component.(js|ts)`), or wherever your mount function is imported, make the following update to add `@`.

<Badge type="danger">Before</Badge>{' '}

```ts
import { mount } from `cypress/angular`
```

<Badge type="success">After</Badge>

```ts
import { mount } from `@cypress/angular`
```

### Selector Playground API changes

The `Cypress.SelectorPlayground` API has been renamed to
[`Cypress.ElementSelector`](/api/cypress-api/element-selector-api). Additionally, the `onElement` function has been removed as an option to the `defaults` method.

This change was made in order to reflect its use in features beyond just the Selector Playground - like Cypress Studio.

The following code shows how to migrate from the `Cypress.SelectorPlayground` API to the
[`Cypress.ElementSelector`](/api/cypress-api/element-selector-api) API.

<Badge type="danger">Before</Badge>{' '}

```ts
Cypress.SelectorPlayground.defaults({
  selectorPriority: ['class', 'id'],
})
```

<Badge type="success">After</Badge>

```ts
Cypress.ElementSelector.defaults({
  selectorPriority: ['class', 'id'],
})
```

## Migrating to Cypress 14.0

This guide details the code changes needed to migrate to Cypress
version 14.
[See the full changelog for version v14.0](/app/references/changelog#14-0-0).

### Node.js 18+ support

Cypress comes bundled with its own
[Node.js version](https://github.com/cypress-io/cypress/blob/develop/.node-version).
However, installing the `cypress` npm package uses the Node.js version installed
on your system.

[See Node's release schedule](https://github.com/nodejs/Release). Node.js
version 16 and 21 will no longer be supported when installing Cypress. The minimum Node.js
version supported to install Cypress is Node.js 18+.

### Unsupported Linux Distributions

Prebuilt binaries for Linux are no longer compatible with Linux distributions based on glibc `<2.28`.
This support is in line with Node.js's support for Linux in 18+.

If you're using a Linux distribution based on glibc `<2.28`, for example, Ubuntu 14-18, RHEL 7, CentOS 7, Amazon Linux 2, you'll need to
update your system to a newer version to install Cypress 14+.

### Minimum macOS 11 (Big Sur)

[Cypress 14.0](/app/references/changelog#14-0-0) upgrades Electron to `33.2.1`.
On macOS this requires a minimum version of macOS 11 (Big Sur).

If you're using a lower version of macOS make sure that you update.

### Updated Browser Support

Starting in Cypress 14, Cypress will officially support [the latest 3 major versions of Chrome, Firefox, and Edge](/app/references/launching-browsers#Browser-versions-supported).

Older browser versions may still work with Cypress, but we recommend keeping your browsers up to date to ensure compatibility with Cypress.

### Changes to `cy.origin()`

To account for Chrome's [impending deprecation](https://developer.chrome.com/blog/document-domain-setter-deprecation) of setting `document.domain`, and to support sites that use [origin-keyed agent clusters](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin-Agent-Cluster),
Cypress no longer injects `document.domain` into `text/html` content by default.

Because of this, tests that visit more than one origin (defined as a composite of the URL _scheme_, _hostname_, and _port_) must now use `cy.origin()`.
Without `cy.origin()`, interacting with a second origin in the same test will cause the test to fail, even if the two origins
are in the same superdomain. This means you must now use `cy.origin()` in more situations than before.

{/* prettier-ignore-start */}
<Icon name="exclamation-triangle" color="red" /> **Failing Test**
```js
cy.visit('https://www.cypress.io')
cy.visit('https://docs.cypress.io')
// Cypress will not be able to interact with the page, causing the test to fail
cy.get('[role="banner"]').should('be.visible')
```

<Icon name="check-circle" color="green" /> **Fixed Test**

```js
cy.visit('https://www.cypress.io')
cy.visit('https://docs.cypress.io')
cy.origin('https://docs.cypress.io', () => {
  cy.get('[role="banner"]').should('be.visible')
})
```
{/* prettier-ignore-end */}

:::info

To ease this transition, Cypress v14.0 introduced the ["injectDocumentDomain" configuration option](/app/references/configuration#injectDocumentDomain). When this option
is set to true, `cy.origin()` will not be required to navigate between origins, as long as the superdomain matches.

<Icon name="exclamation-triangle" /> If `injectDocumentDomain` is set to `true`,
Cypress will warn that this option is deprecated.

<Icon name="exclamation-triangle" /> `injectDocumentDomain` will be removed in a
future version of Cypress.

<Icon name="exclamation-triangle" /> Setting `injectDocumentDomain` to `true` may
cause certain sites to stop working in Cypress. Please read the [configuration notes](/app/references/configuration#injectDocumentDomain)
before use.

<Icon name="exclamation-triangle" /> If your test suites require `experimentalWebKitSupport`,
`injectDocumentDomain` must be set to `true`.

<Icon name="exclamation-triangle" /> Chrome may remove support for `document.domain`
at any time; if this configuration option is enabled, Cypress may cease to work in
Chrome at any time. If this occurs, Chrome will raise an issue in its developer tools
indicating that the deprecated `document.domain` is in use. To resolve this issue,
set the `injectDocumentDomain` option to `false` and issue any newly necessary `cy.origin()`
commands.{' '}

:::

### Deprecation of `resourceType` on `cy.intercept`

The `resourceType` option on [`cy.intercept`](/api/commands/intercept) has been deprecated in Cypress 14.0.0. We anticipate the types of the `resourceType` to change in the future or be completely removed
from the API.

Our intention is to replace essential functionality dependent on the `resourceType` within Cypress in a future version (like [hiding network logs that are not fetch/xhr](/api/commands/intercept#Disabling-logs-for-a-request)). If you're using `resourceType` in your tests, please leave feedback on which `resourceType` values are important to you in this [GitHub issue](https://github.com/cypress-io/cypress/issues/30447).

### CT Just in Time Compile changes

In Cypress 13.14.0, we released an experimental flag, `experimentalJustInTimeCompile`,
to enable Just in Time (JIT) compilation for Component Testing with `vite` and `webpack`. The response from this change was positive and we've made a few changes in response:

- JIT compilation is the default behavior for component tests as a [`justInTimeCompile`](/app/references/configuration#component) component configuration option.
- JIT compilation no longer applies with `vite`, since there is no benefit to enabling this with `vite`.

This option will only compile resources directly related to your spec, compiling them 'just-in-time' before spec execution. This should result in improved memory management and performance for component tests in `cypress open` and `cypress run` modes, especially for large test suites.

#### Disable JIT Compilation

If you would like to disable JIT compilation, you can do so by setting [`justInTimeCompile`](/app/references/configuration#component) to `false` in your component configuration.

:::cypress-config-example

```js
{
  component: {
    justInTimeCompile: false
  }
}
```

:::

For users with the existing `experimentalJustInTimeCompile` flag set, you can remove this flag from your configuration.

### React `<18` CT no longer supported

With [LTS ending](https://github.com/reactjs/react.dev/issues/1745#issuecomment-466767389) for React 16 and 17 several years ago, the minimum required React version for component testing is now `18.0.0`.

Now that the minimum version of React supported for Component Testing is `18.0.0`, Cypress is able to merge the `cypress/react18` test harness into the main `cypress/react` test harness. Because of this, the `@cypress/react18` harness is deprecated and no longer shipped with the binary. Support has been moved to `cypress/react`.

To migrate, change the test harness from `cypress/react18` to `cypress/react`.

<Badge type="danger">Before</Badge>{' '}

```ts
import { mount } from 'cypress/react18'
```

<Badge type="success">After</Badge>

```ts
import { mount } from 'cypress/react'
```

#### To continue using React below v18

If you haven't been able to migrate away from an older React version and still need that test harness, it can be installed independently via the [`@cypress/react`](https://www.npmjs.com/package/@cypress/react) `8.x.x` package from `npm`.

Note that this test harness version is deprecated and no longer supported by Cypress. This version is intended to serve as a temporary workaround to migrate your project to React v18+.

```sh
npm install --save-dev @cypress/react@8
```

Inside your support file (ex: `./cypress/support/component.(js|ts)`), or wherever your mount function is imported, make the following update to add `@`.

<Badge type="danger">Before</Badge>{' '}

```ts
import { mount } from 'cypress/react'
```

<Badge type="success">After</Badge>

```ts
import { mount } from '@cypress/react'
```

### Angular `<17.2.0` CT no longer supported

With [LTS ending](https://angular.dev/reference/releases#actively-supported-versions) for Angular 16, the minimum required Angular version for component testing is now `17.2.0` in order to support [signals](/app/component-testing/angular/examples#Signals) as a first class citizen.

Now that the minimum version of Angular supported for Component Testing is `17.2.0`, Cypress is able to merge the `cypress/angular-signals` test harness into the main `cypress/angular` test harness. Because of this, the `@cypress/angular-signals` harness is deprecated and no longer shipped with the binary. Support has been moved to `cypress/angular`.

To migrate, just change the test harness from `cypress/angular-signals` to `cypress/angular`.

<Badge type="danger">Before</Badge>{' '}

```ts
import { mount } from 'cypress/angular-signals'
```

<Badge type="success">After</Badge>

```ts
import { mount } from 'cypress/angular'
```

#### To continue using Angular below v17.2.0

If you haven't been able to migrate away from an older Angular version and still need that test harness, it can be installed independently via the [`@cypress/angular`](https://www.npmjs.com/package/@cypress/angular) `2.x.x` package from `npm`.

Note that this test harness version is deprecated and no longer supported by Cypress. This version is intended to serve as a temporary workaround to migrate your project to Angular v17.2.0+.

```sh
npm install --save-dev @cypress/angular@2
```

Inside your support file (ex: `./cypress/support/component.(js|ts)`), or wherever your mount function is imported, make the following update to add `@`.

<Badge type="danger">Before</Badge>{' '}

```ts
import { mount } from 'cypress/angular'
```

<Badge type="success">After</Badge>

```ts
import { mount } from '@cypress/angular'
```

### Vue 2 CT no longer supported

[Vue 2 reached end-of-life on December 31st, 2023](https://v2.vuejs.org/eol/). With Cypress 14, Cypress no longer ships the Vue 2 component testing harness with the Cypress binary.

#### To continue using Vue 2

If you haven't been able to migrate away from Vue 2 and still need that test harness, it can be installed independently via the [@cypress/vue2](https://www.npmjs.com/package/@cypress/vue2) package.

Note that this test harness is deprecated and no longer supported by Cypress. This package is intended to serve as a temporary workaround to migrate your project to Vue 3. The Cypress launchpad will warn against Component testing mismatched dependencies, but this will not stop you from running your component tests.

```sh
npm install --save-dev @cypress/vue2
```

Inside your support file (ex: `./cypress/support/component.(js|ts)`), or wherever your mount function is imported, make the following update to add `@`.

<Badge type="danger">Before</Badge>{' '}

```ts
import { mount } from 'cypress/vue2'
```

<Badge type="success">After</Badge>

```ts
import { mount } from '@cypress/vue2'
```

### Create React App CT no longer supported

[create-react-app](https://create-react-app.dev/) is no longer actively maintained or supported (see [CRA issue #13393](https://github.com/facebook/create-react-app/issues/13393)). Your component tests will now need a bundler to run. If still using [create-react-app](https://create-react-app.dev/), you'll either need to:

- [Eject](https://create-react-app.dev/docs/available-scripts/#npm-run-eject) the configuration to bundle with webpack.
- Leverage [vite](https://vite.dev/guide/) to bundle your component tests (quick setup with [create-vite](https://github.com/vitejs/vite/tree/main/packages/create-vite)).

After selecting a bundler, change the `framework` option in your Cypress config from `create-react-app` to `react`. If ejecting the `create-react-app`, change your cypress config to look something like this:

```js
process.env.NODE_ENV = 'development'
const { defineConfig } = require('cypress')
const webpackConfig = require('./config/webpack.config.js')

module.exports = defineConfig({
  component: {
    devServer: {
      framework: 'react',
      bundler: 'webpack',
      webpackConfig: webpackConfig('development'),
    },
  },
})
```

### `@vue/cli-service` CT no longer supported

`@vue/cli-service` is in [maintenance mode](https://cli.vuejs.org/guide/cli-service.html) and is no longer maintained by the Vue core team. Your component tests will now need a bundler to run. If still using [Vue CLI](https://cli.vuejs.org/), you will either need to:

- Migrate to webpack ([see example](https://github.com/cypress-io/cypress-component-testing-apps/tree/main/vue3-webpack-ts)).
- Leverage [vite](https://vite.dev/guide/). The Vue team recommends migrating to using `create-vue` to scaffold a [Vite](https://vite.dev/)-based project.

After selecting a bundler, change the `framework` option in your Cypress config from `"vue-cli"` to `"vue"`. Your Cypress configuration should change as outlined below.

<Badge type="danger">Before</Badge>

:::cypress-config-example

```
{
  component: {
    devServer: {
      framework: 'vue-cli',
      bundler: 'webpack',
    },
  }
}
```

:::

<Badge type="success">After</Badge>

:::cypress-config-example

```
{
  component: {
    devServer: {
      framework: 'vue',
      bundler: 'vite', // or 'webpack'
    },
  }
}
```

:::

### Svelte 3 & 4 CT no longer supported

With Cypress 14, Cypress no longer ships the Svelte 3 and 4 component testing harness with the Cypress binary.

However, if you have not been able to upgrade Svelte and still need the Cypress Svelte 3 and 4 test harness, it can be installed independently via version 2.x.x of the [@cypress/svelte](https://www.npmjs.com/package/@cypress/svelte) package.

```sh
npm install --save-dev @cypress/svelte@2
```

Note that this version of the test harness is deprecated and no longer actively supported by Cypress and is intended to serve as a temporary work around until you are able to migrate your project to Svelte 5+. The Cypress launchpad will also warn against Component testing mismatched dependencies, but this will not stop you from running your component tests.

To update, inside your support file (ex: `./cypress/support/component.(js|ts)`) or wherever your mount function is imported, change

```ts
import { mount } from 'cypress/svelte'
```

to

```ts
import { mount } from '@cypress/svelte'
```

Your code should now look like this:

```ts
import MySvelteComponent from './MySvelteComponent'
import { mount } from '@cypress/svelte'

it('renders', () => {
  cy.mount(MySvelteComponent)
})
```

## Migrating to Cypress 13.0

This guide details the changes and how to change your code to migrate to Cypress
version 13.
[See the full changelog for version v13.0](/app/references/changelog#13-0-0).

### Cypress Cloud Test Replay

[Test Replay](/cloud/features/test-replay) is enabled by default in `v13` of the Cypress App.

You may need to allowlist `capture.cypress.io` if you work with a strict VPN. See our FAQ section about [VPN subdomain allowlisting](/cloud/faq#Im-working-with-a-restrictive-VPN-Which-subdomains-do-I-have-to-allow-on-my-VPN-for-Cypress-Cloud-to-work-properly).

With Test Replay enabled, the Cypress Runner UI is hidden by default when recording a run to the Cloud. If the Runner UI is needed during the run, you can enable it by passing [`--runner-ui`](/app/references/command-line#cypress-run-runner-ui) to the [`cypress run`](/app/references/command-line#cypress-run) command.

You can [opt-out](/cloud/features/test-replay#Opt-out-of-Test-Replay) of this feature in Cloud project-level settings.

### Video updates

#### `video` is set to `false` by default

You can continue recording video by setting `video` to `true` either in your Cypress configuration or via [overriding options](/app/references/configuration#Overriding-Options). This can be useful if you want video locally or want video for some other reason, like in non-Chromium browsers where [Test Replay](/cloud/features/test-replay) is not available.

:::cypress-config-example

```ts
{
  video: true
}
```

:::

#### `videoUploadOnPasses` configuration option has been removed

Most users used `videoUploadOnPasses` as a way to skip the time to compress and upload videos to the Cloud. Since we're turning off `videoCompression` by default, this configuration option does not offer the time saving value that it once would.

If you want to prevent a passing test from uploading to the Cloud, we recommend deleting the video using our [guide with code examples to discard captured video of passing tests](/app/guides/screenshots-and-videos#Control-which-videos-to-keep-and-upload-to-Cypress-Cloud).

#### `videoCompression` is set to `false` by default

Cypress has the capability to compress recorded videos after a run to reduce the video file size. By default, compression is now turned off. This results in a reduced run time by removing the time to compress the video, a larger video file size and better video quality.

You can enable this with the `videoCompression` [configuration](/app/references/configuration#Videos) option if you'd like to reduce the video file size for any reason. This will also reduce the video quality and take slightly longer to process and complete the run.

:::cypress-config-example

```ts
{
  // value can be true/false -or- an integer between 0 and 51
  videoCompression: true,
}
```

:::

### `cy.readFile()` is now a query command

In Cypress `v13`, the [`.readFile()`](/api/commands/readfile) command is now a query.
Tests written using it should continue to operate exactly as before; no changes
are necessary.

`readFile()` will re-read the file from disk if any upcoming command in the same
chain fails. Assertions no longer have to be directly attached.

```js
cy.readFile(`users.json`).its('users.123.fullName').should('eq', 'John Doe')
```

Beginning with Cypress `v13`, the above test will re-read the file until the file
exists, it has the requested property, and it passes the assertion.

In previous versions of Cypress, the above command would retry until the file
existed, but would _not_ re-read it from disk if the file didn't have the
requested property or the contents didn't match.

#### `.readFile()` can no longer be overwritten with `Cypress.Commands.overwrite()`

Queries must be overwritten using `Cypress.Commands.overwriteQuery()`. If you
were previously overwriting `cy.readFile()`, you will need to update your code
to use `Cypress.Commands.overwriteQuery('readFile', function() { ... })` rather
than `Cypress.Commands.overwrite('readFile', () => { ... })`. For more details
on overwriting queries, see the
[Overwriting Existing Queries](/api/cypress-api/custom-queries#Overwriting-Existing-Queries).

## Migrating to Cypress 12.0

This guide details the changes and how to change your code to migrate to Cypress
version 12.0.
[See the full changelog for version 12.0](/app/references/changelog#12-0-0).

The Session and Origin experiment has been released as General Availability
(GA), meaning that we have deemed this experiment to be feature complete and
free of issues in the majority of use cases. With releasing this as GA, the
`experimentalSessionAndOrigin` flag has been removed, the
[`cy.origin()`](/api/commands/origin) and
[`cy.session()`](/api/commands/session) commands are generally available and
[Test Isolation](/app/core-concepts/writing-and-organizing-tests#Test-Isolation)
is enabled by default.

### Node.js 14+ support

Cypress comes bundled with its own
[Node.js version](https://github.com/cypress-io/cypress/blob/develop/.node-version).
However, installing the `cypress` npm package uses the Node.js version installed
on your system.

Node.js 12 reached its end of life on April 30, 2022.
[See Node's release schedule](https://github.com/nodejs/Release). This Node.js
version will no longer be supported when installing Cypress. The minimum Node.js
version supported to install Cypress is Node.js 14+.

### Test Isolation

The
[`testIsolation`](/app/core-concepts/writing-and-organizing-tests#Test-Isolation)
config option is enabled by default. This means Cypress resets the browser
context _before_ each test by:

- clearing the dom state by visiting `about:blank`
- clearing [cookies](/api/cypress-api/cookies) in all domains
- clearing
  [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)
  in all domains
- clearing
  [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage)
  in all domains

Test suites that relied on the application to persist between tests may have to
be updated to revisit their application and rebuild the browser state for each
test that needs it.

Before this change, it was possible to write tests such that you could rely on
the application (i.e. DOM state) to persist between tests. For example you could
log in to a CMS in the first test, change some content in the second test,
verify the new version is displayed on a different URL in the third, and log out
in the fourth.

Here's a simplified example of such a test strategy.

<Badge type="danger">Before</Badge> Multiple small tests against different origins

```js
it('logs in', () => {
  cy.visit('https://example.cypress.io')
  cy.get('input#password').type('Password123!')
  cy.get('button#submit').click()
})

it('updates the content', () => {
  // already on page redirect from clicking button#submit
  cy.get('#current-user').contains('logged in')
  cy.get('button#edit-1').click()
  cy.get('input#title').type('Updated title')
  cy.get('button#submit').click()
  cy.get('.toast').contains('Changes saved!')
})

it('validates the change', () => {
  cy.visit('/items/1')
  cy.get('h1').contains('Updated title')
})
```

After migrating, when `testIsolation=true` by default, this flow would need to
be contained within a single test. While the above practice has always been
[discouraged](/app/core-concepts/best-practices#Having-Tests-Rely-On-The-State-Of-Previous-Tests)
we know some users have historically written tests this way, often to get around
the `same-origin` restrictions. But with [`cy.origin()`](/api/commands/origin)
you no longer need these kind of brittle hacks, as your multi-origin logic can
all reside in a single test, like the following.

<Badge type="success">After</Badge> One big test using `cy.origin()`

```js
it('securely edits content', () => {
  cy.origin('cypress.io', () => {
    cy.visit('https://example.cypress.io')
    cy.get('input#password').type('Password123!')
    cy.get('button#submit').click()
  })

  cy.origin('cypress-dx.com', () => {
    cy.url().should('contain', 'cms')
    cy.get('#current-user').contains('logged in')
    cy.get('button#edit-1').click()
    cy.get('input#title').type('Updated title')
    cy.get('button#submit').click()
    cy.get('.toast').contains('Changes saved!')
  })

  cy.visit('/items/1')
  cy.get('h1').contains('Updated title')
})
```

The just-released `cy.session()` command can be used to setup and cache cookies,
local storage and session storage between tests to easily re-establish the
previous (or common) browser contexts needed in a suite. This command will run
setup on its initial execution and will restore the saved browser state on each
sequential command execution. This command reduces the need for repeated
application logins, while users also benefit from the test isolation guardrails
to write independent, reliable and deterministic tests from the start.

If for whatever reason you still need to persist the dom and browser context
between tests, you can disable test isolation by setting `testIsolation=false`
on the root configuration or at the suite-level. For example:

```js
describe('workflow', { testIsolation: false }, () => {
  ...
})
```

It is important to note that while disabling test isolation may improve the
overall performance of end-to-end tests, it can cause state to "leak" between
tests. This can make later tests dependent on the results of earlier tests, and
potentially cause misleading test failures. It is important to be extremely
mindful of how tests are written when using this mode, and ensure that tests
continue to run independently of one another.

<Badge type="danger">For example</Badge> the following tests are not independent
nor deterministic:

```js
describe('workflow', { testIsolation: false }, () => {
  it('logs in', () => {
    cy.visit('https://example.cypress.io/log-in')
    cy.get('username').type('User1')
    cy.get('password').type(Cypress.env('User1_password'))
    cy.get('button#login').click()
    cy.contains('User1')
  })

  it('clicks user profile', () => {
    cy.get('User1').find('#profile_avatar').click()
    cy.contains('Email Preferences')
  })

  it('updates profile', () => {
    cy.get('button#edit')
    cy.get('email').type('user1@email.com')
    cy.get('button#save').click()
  })
})
```

In the above example, each test is relying on the previous test to be
_successful_ to correctly execute. If at any point, the first or second test
fails, the sequential test(s) will automatically fail and provide unreliable
debugging errors since the errors are representative of the previous test.

The best way to ensure your tests are independent is to add a `.only()` to your
test and verify it can run successfully without the test before it.

#### Simulating Pre-Test Isolation Behavior

Test isolation did not truly exist pre-12. Pre-Cypress 12, the behavior was a
hybrid of both `testIsolation` enabled and disabled. All local storage and
cookies on the current domain were cleared, but Cypress did not clear session
storage and the page always persisted.

In Cypress 12+ when `testIsolation` is enabled, local storage, session storage
and cookies in **all** domains are cleared and the page is cleared. When
`testIsolation` is disabled, nothing is cleared before the next test so all
local storage, session storage and cookies & the page persists.

If you wanted to match pre-Cypress 12 behavior, you need to disable
`testIsolation`, then run `cy.clearLocalStorage()` and `cy.clearCookies()` in a
beforeEach hook to clear the local storage and cookies in the current domain.

```js
describe('match pre-12 behavior', { testIsolation: false }, () => {
  beforeEach(() => {
    cy.clearLocalStorage()
    cy.clearCookies()
    // other beforeEach logic to restore the expected local storage or cookies needed on the client.
  })
})
```

Many of the issues test isolation solved were around cookie management with
tests trying to save and persist cookies because the page was still available,
but the cookies on the domain were unexpectedly cleared which broke interactions
with the application. It wasn’t obvious Cypress was doing a partial browser
clean up. Explicitly setting test isolation to enabled or disabled allows you to
choose what is right for your tests.

### Behavior Changes in Alias Resolution

Cypress always re-queries aliases when they are referenced. This can result in
certain tests that used to pass could start to fail. For example,

```js
cy.findByTestId('popover')
  .findByRole('button', { expanded: true })
  .as('button')
  .click()

cy.get('@button').should('have.attr', 'aria-expanded', 'false')
```

previously passed, because the initial button was collapsed when first queried,
and then later expanded. However, in Cypress 12, this test fails because the
alias is always re-queried from the DOM, effectively resulting in the following
execution:

```js
cy.findByTestId('popover').findByRole('button', { expanded: true }).click()

cy.findByTestId('popover')
  .findByRole('button', { expanded: true }) // A button which matches here (is expanded)...
  .should('have.attr', 'aria-expanded', 'false') // ...will never pass this assertion.
```

You can rewrite tests like this to be more specific; in our case, we changed the
alias to be the first button rather than the unexpanded button.

```js
cy.findByTestId('popover').findAllByRole('button').first().as('button')
```

If you want to alias a static value, such that it is never re-queried, you will
need Cypress [12.3.0](/app/references/changelog#12-3-0) or later, which
introduced the `type` option for [`.as()`](/api/commands/as) to opt into the old
behavior.

```
cy.get('.username').invoke('val').as('username', { type: 'static' })
```

See [`.as()`](/api/commands/as) for more details.

### Command / Cypress API Changes

#### `Cypress.Cookies.defaults` and `Cypress.Cookies.preserveOnce`

The `Cypress.Cookies.defaults` and `Cypress.Cookies.preserveOnce` APIs been
removed. Use the [`cy.session()`](/api/commands/session) command to preserve
cookies (and local and session storage) between tests.

If you were using `Cypress.Cookies.preserveOnce` to preserve a specific cookie
within a single spec, this might look like the following:

```diff
describe('Dashboard', () => {
  beforeEach(() => {
-    cy.login()
-    Cypress.Cookies.preserveOnce('session_id', 'remember_token')
+    cy.session('unique_identifier', cy.login, {
+       validate () {
+        cy.getCookies().should('have.length', 2)
+       },
+    })
  })
```

If you were using `Cypress.Cookies.defaults` to preserve a cookie or set of
cookies across test, this might look like the following:

```diff
describe('Dashboard', () => {
  beforeEach(() => {
-    cy.login()
-    Cypress.Cookies.defaults({
-       preserve: ['session_id', 'remember_token']
-    })
+    cy.session('unique_identifier', cy.login, {
+       validate () {
+        cy.getCookies().should('have.length', 2)
+       },
+       cacheAcrossSpecs: true
+    })
  })
```

#### `cy.server()`, `cy.route()` and `Cypress.Server.defaults`

The` cy.server()` and` cy.route()` commands and the `Cypress.server.defaults`
API has been removed. Use the [`cy.intercept()`](/api/commands/intercept)
command instead.

```diff
  it('can encode + decode headers', () => {
-   Cypress.Server.defaults({
-     delay: 500,
-     method: 'GET',
-   })
-   cy.server()
-   cy.route(/api/, () => {
-      return {
-        'test': 'We’ll',
-      }
-    }).as('getApi')
+   cy.intercept('GET', /api/, (req) => {
+      req.on('response', (res) => {
+        res.setDelay(500)
+      })
+      req.body.'test': 'We’ll'
+    }).as('getApi')
    cy.visit('/index.html')
    cy.window().then((win) => {
      const xhr = new win.XMLHttpRequest
      xhr.open('GET', '/api/v1/foo/bar?a=42')
      xhr.send()
    })

    cy.wait('@getApi')
-   .its('url').should('include', 'api/v1')
+   .its('request.url').should('include', 'api/v1')
  })
```

#### `.invoke()`

The [`.invoke()`](/api/commands/invoke) command now throws an error if the
function returns a promise. If you wish to call a method that returns a promise
and wait for it to resolve, use [`.then()`](/api/commands/then) instead of
`.invoke()`.

```diff
cy.wrap(myAPI)
-  .invoke('makeARequest', 'http://example.com')
+  .then(api => api.makeARequest('http://example.com'))
   .then(res => { ...handle response... })
```

If `.invoke()` is followed by additional commands or assertions, it will call
the named function multiple times. This has the benefit that the chained
assertions can more reliably use the function's return value.

If this behavior is undesirable because you expect the function to be invoked
only once, break the command chain and move the chained commands and/or
assertions to their own chain. For example, rewrite

```diff
- cy.get('input').invoke('val', 'text').type('newText')
+ cy.get('input').invoke('val', 'text')
+ cy.get('input').type('newText')
```

#### `.should()`

The [`.should()`](/api/commands/should) assertion now throws an error if Cypress
commands are invoked from inside a `.should()` callback. This previously
resulted in unusual and undefined behavior. If you wish to execute a series of
commands on the yielded value, use`.then()` instead.

```diff
cy.get('button')
-  .should(($button) => {

    })
+  .then(api => api.makeARequest('http://example.com'))
   .then(res => { ...handle response... })
```

#### `.within()`

The [`.within()`](/api/commands/within) command now throws an error if it is
passed multiple elements as the subject. This previously resulted in
inconsistent behavior, where some commands would use all passed in elements,
some would use only the first and ignore the rest, and
[`.screenshot()`](/api/commands/screenshot) would throw an error if used inside
a `.within()` block with multiple elements.

If you were relying on the old behavior, you have several options depending on
the desired result.

The simplest option is to reduce the subject to a single element.

```diff
cy.get('tr')
+  .first() // Limit the subject to a single element before calling .within()
  .within(() => {
    cy.contains('Edit').click()
  })
```

If you have multiple subjects and wish to run commands over the collection as a
whole, you can alias the subject rather than use `.within()`.

```diff
cy.get('tr')
-  .within(() => {
-    cy.get('td').should('have.class', 'foo')
-    cy.get('td').should('have.class', 'bar')
-  })
+  .as('rows') // Store multiple elements as an alias

+cy.get('@rows').find('td').should('have.class', 'foo')
+cy.get('@rows').find('td').should('have.class', 'bar')
```

Or if you have a collection and want to run commands over every element, use
`.each()` in conjunction with `.within()`.

```diff
cy.get('tr')
-  .within(() => {
-    cy.contains('Edit').should('have.attr', 'disabled')
-  })
+  .each($tr => {
+    cy.wrap($tr).within(() => {
+      cy.contains('Edit').should('have.attr', 'disabled')
+    })
+  })
```

#### `Cypress.Commands.overwrite()`

In Cypress 12.0.0, we introduced a new command type, called queries. A query is
a small and fast command for getting data from the window or DOM. This
distinction is important because Cypress can retry chains of queries, keeping
the yielded subject up-to-date as a page rerenders.

With the introduction of query commands, the following commands have been
re-categorized and can no longer be overwritten with
[`Cypress.Commands.overwrite()`](/api/cypress-api/custom-commands#Overwrite-Existing-Commands):

- [`.as()`](/api/commands/as)
- [`.children()`](/api/commands/children)
- [`.closest()`](/api/commands/closest)
- [`.contains()`](/api/commands/contains)
- [`cy.debug()`](/api/commands/debug)
- [`cy.document()`](/api/commands/document)
- [`.eq()`](/api/commands/eq)
- [`.filter()`](/api/commands/filter)
- [`.find()`](/api/commands/find)
- [`.first()`](/api/commands/first)
- [`.focused()`](/api/commands/focused)
- [`.get()`](/api/commands/get)
- [`.hash()`](/api/commands/hash)
- [`.its()`](/api/commands/its)
- [`.last()`](/api/commands/last)
- [`cy.location()`](/api/commands/location)
- [`.next()`](/api/commands/next)
- [`.nextAll()`](/api/commands/nextall)
- [`.not()`](/api/commands/not)
- [`.parent()`](/api/commands/parent)
- [`.parents()`](/api/commands/parents)
- [`.parentsUntil()`](/api/commands/parentsuntil)
- [`.prev()`](/api/commands/prev)
- [`.prevUntil()`](/api/commands/prevuntil)
- [`cy.root()`](/api/commands/root)
- [`.shadow()`](/api/commands/shadow)
- [`.siblings()`](/api/commands/siblings)
- [`cy.title()`](/api/commands/title)
- [`cy.url()`](/api/commands/url)
- [`cy.window()`](/api/commands/window)

If you were previously overwriting one of the above commands, try adding your
version as a new command using
[`Cypress.Commands.add()`](/api/cypress-api/custom-commands) under a different
name.

## Migrating to Cypress 11.0

This guide details the changes and how to change your code to migrate to Cypress
version 11.0.
[See the full changelog for version 11.0](/app/references/changelog#11-0-0).

### Component Testing Updates

As of Cypress 11, Component Testing is now generally available. There are some
minor breaking changes. Most projects should be able to migrate without any code
modifications.

#### Changes to Mounting Options

Each major library we support has a `mount` function with two arguments:

1. The component
2. Mounting Options

Mounting options previously had several properties that are now removed:

- cssFile, cssFiles
- style, styles
- stylesheet, stylesheets

Read more about the rationale
[here](https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/).
We recommend writing test-specific styles in a separate `css` file you import in
your test, or in your `supportFile`.

#### Before (Cypress 10)

```jsx
import { mount } from 'cypress/react'
import { Card } from './Card'

it('renders some content', () => {
  cy.mount(<Card title="title" />, {
    styles: `
      .card { width: 100px; }
    `,
    stylesheets: [
      'https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css',
    ],
  })
})
```

#### After (Cypress 11)

```js
/** style.css */
@import "https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css";
.card { width: 100px }

/** Card.cy.jsx */
import { mount } from 'cypress/react'
import { Card } from './Card'
import './styles.css' // contains CDN link and custom styling.

it('renders some content', () => {
  cy.mount(<Card title="title" />)
})
```

### React - `mountHook` Removed

`mountHook` from `cypress/react` has been removed. Read more about the rationale
[here](https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/).

We recommend simply replacing it with `mount` and a component.

Consider the following `useCounter` hook:

```js
import { useState, useCallback } from 'react'

function useCounter() {
  const [count, setCount] = useState(0)
  const increment = useCallback(() => setCount((x) => x + 1), [])

  return { count, increment }
}
```

#### Before - Cypress 10 and `mountHook`

```js
import { mountHook } from 'cypress/react'
import { useCounter } from './useCounter'

it('increments the count', () => {
  mountHook(() => useCounter()).then((result) => {
    expect(result.current.count).to.equal(0)
    result.current.increment()
    expect(result.current.count).to.equal(1)
    result.current.increment()
    expect(result.current.count).to.equal(2)
  })
})
```

#### After - Cypress 11 and `mount`

```js
import { useCounter } from './useCounter'

it('increments the count', () => {
  function Counter() {
    const { count, increment } = useCounter()
    return (
      <>
        <h1 name="count">Count is {{ count }}</h1>
        <button onClick={increment}>Increment</button>
      </>
    )
  }

  cy.mount(<Counter />).then(() => {
    cy.get('[name="count"]')
      .should('contain', 0)
      .get('button')
      .click()
      .get('[name="count"]')
      .should('contain', 1)
  })
})
```

### React - `unmount` Removed

`unmount` from `cypress/react` has been removed. Read more about the rationale
[here](https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/).
We recommend using the API React provides for unmounting components,
[unmountComponentAtNode](https://reactjs.org/docs/react-dom.html#unmountcomponentatnode).

#### Before - Cypress 10 and `unmount`

```js
import { unmount } from 'cypress/react'

it('calls the prop', () => {
  cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
  cy.contains('My component')

  unmount()

  // the component is gone from the DOM
  cy.contains('My component').should('not.exist')
  cy.get('@onUnmount').should('have.been.calledOnce')
})
```

#### After - Cypress 11 and `unmountComponentAtNode`

```js
import { getContainerEl } from 'cypress/react'
import ReactDom from 'react-dom'

it('calls the prop', () => {
  cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
  cy.contains('My component')

  cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()))

  // the component is gone from the DOM
  cy.contains('My component').should('not.exist')
  cy.get('@onUnmount').should('have.been.calledOnce')
})
```

### Vue - `mountCallback` Removed

`mountCallback` from `cypress/vue` has been removed. Read more about the
rationale
[here](https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/).
We recommend using `mount`.

#### Before - Cypress 10 and `mountCallback`

```js
import { mountCallback } from 'cypress/vue'

beforeEach(mountCallback(MessageList))

it('shows no messages', () => {
  getItems().should('not.exist')
})
```

#### After - Cypress 11 and `mount`

```js
beforeEach(() => cy.mount(MessageList))

it('shows no messages', () => {
  getItems().should('not.exist')
})
```

### Angular - Providers Mounting Options Change

There is one breaking change for Angular users in regards to providers. In
Cypress 10, we took any providers passed as part of the Mounting Options and
overrode the component providers via the `TestBed.overrideComponent` API.

In Cypress 11, providers passed as part of the Mounting Options will be assigned
at the module level using the `TestBed.configureTestingModule` API.

This means that module-level providers (resolved from imports or
`@Injectable({ providedIn: 'root' })` can be overridden, but providers specified
in `@Component({ providers: [...] })` will not be overridden when using
`cy.mount(MyComponent, { providers: [...] })`.

To override component-level providers, use the `TestBed.overrideComponent` API.

See a concrete example
[here](https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#angularproviders-priority).

### Vite Dev Server (`cypress/vite-dev-server`)

When providing an inline `viteConfig` inside of `cypress.config`, any
`vite.config.js` file is not automatically merged.

#### Before - Cypress 10 and `viteConfig`

```js
import { defineConfig } from 'cypress'

export default defineConfig({
  component: {
    devServer: {
      framework: 'react',
      bundler: 'vite',
      viteConfig: {
        // ... custom vite config ...
        // result merged with `vite.config` file if present
      },
    },
  },
})
```

#### After - Cypress 11 and `viteConfig`

```js
import { defineConfig } from 'cypress'
import viteConfig from './vite.config'

export default defineConfig({
  component: {
    devServer: {
      framework: 'react',
      bundler: 'vite',
      viteConfig: {
        ...viteConfig,
        // ... other overrides ...
      },
    },
  },
})
```

Vite 3+ users could make use of the
[`mergeConfig`](https://vitejs.dev/guide/api-javascript.html#mergeconfig) API.

## Migrating to Cypress 10.0

This guide details the changes and how to change your code to migrate to Cypress
version 10.0.
[See the full changelog for version 10.0](/app/references/changelog#10-0-0).

<DocsVideo
  src="https://youtube.com/embed/mIqKNhLlPcU"
  title="Migrating to Cypress 10.0"
/>

### Cypress Changes

- The "Run all specs" and "Run filtered specs" functionality have been removed.
- The experimental "Cypress Studio" has been removed and will be
  rethought/revisited in a later release.
- Unsupported browser versions can no longer be run via `cypress run` or
  `cypress open`. Instead, an error will display.
- In 9.x and earlier versions, `cypress open` would bring you directly to the
  project specs list. In 10.0.0, you must pass `--browser` and `--e2e` or
  `--component` as well to launch Cypress directly to the specs list.

### Configuration File Changes

Cypress now supports JavaScript and TypeScript configuration files. By default,
Cypress will automatically load a `cypress.config.js` or `cypress.config.ts`
file in the project root if one exists. The
[Configuration guide](/app/references/configuration) has been updated to
reflect these changes, and explains them in greater detail.

Because of this, support for `cypress.json` has been removed since Cypress `v10`.

Related notes:

- If no config file exists when you open Cypress, the automatic set up process
  will begin and either a JavaScript or TypeScript config file will be created
  depending on what your project uses.
- You may use the `--config-file` command line flag or the `configFile`
  [module API](/app/references/module-api) option to specify a `.js` or `.ts`
  file. JSON config files are no longer supported.
- Cypress now requires a config file, so specifying `--config-file false` on the
  command line or a `configFile` value of `false` in the module API is no longer
  valid.
- You can't have both `cypress.config.js` _and_ `cypress.config.ts` files. This
  will result in an error when Cypress loads.
- A
  [`defineConfig()`](/app/references/configuration#Intelligent-Code-Completion)
  helper function is now exported by Cypress, which provides automatic code
  completion for configuration in many popular code editors. For TypeScript
  users, the `defineConfig` function will ensure the configuration object passed
  into it satisfies the type definition of the configuration file.
- Many pages and examples throughout the documentation have been updated to show
  configuration using `cypress.config.js` and `cypress.config.ts` vs the older
  `cypress.json`. For example:

:::cypress-config-example

```ts
{
  e2e: {
    baseUrl: 'http://localhost:1234',
  },
}
```

:::

### Plugins File Removed

Because Cypress now supports JavaScript and TypeScript configuration files, a
separate "plugins file" (which used to default to `cypress/plugins/index.js`) is
no longer needed.

Support for the plugins file has been removed, and it has been replaced with the
new [`setupNodeEvents()`](/app/references/configuration#setupNodeEvents) and
[`devServer`](/app/references/configuration#devServer) config options.

Related notes:

- The `cypress/plugins/index.js` plugins file is no longer automatically loaded
  by Cypress.
- The [`setupNodeEvents()`](/app/references/configuration#setupNodeEvents)
  config option is functionally equivalent to the function exported from the
  plugins file; it takes the same `on` and `config` arguments, and should return
  the same value. See the [Config option changes section](#setupNodeEvents) of
  this migration guide for more details.
- The [`devServer`](/app/references/configuration#devServer) config option is
  specific to component testing, and offers a much more streamlined and
  consistent way to configure a component testing dev server than using the
  plugins file. See the [Config option changes section](#devServer) of this
  migration guide for more details.
- Many pages and examples throughout the documentation have been updated to show
  configuration in `setupNodeEvents` as well as the legacy plugins file. For
  example:

:::cypress-config-plugin-example

```ts
// bind to the event we care about
on('<event>', (arg1, arg2) => {
  // plugin stuff here
})
```

:::

### Config Option Changes

#### `baseUrl`

The `baseUrl` config option is no longer valid at the top level of the
configuration, and may only be defined inside the
[`e2e`](/app/references/configuration#e2e) configuration object.

:::caution

Attempting to set the `baseUrl` config option at the top level of the
configuration will result in an error when Cypress loads.

:::

#### `componentFolder`

The `componentFolder` config option is no longer used, as it has been replaced
by the `specPattern`
[testing-type specific option](/app/references/configuration#Testing-Type-Specific-Options).

:::caution

Attempting to set the `componentFolder` config option will result in an error
when Cypress loads.

:::

#### `devServer`

All functionality related to starting a component testing dev server previously
in the `pluginsFile` has moved here. These options are not valid at the
top-level, and may only be defined in the
[`component`](/app/references/configuration#component) configuration object.

Related notes:

- Do not configure your dev server inside `setupNodeEvents()`, use the
  `devServer` config option instead.

:::info

See the dev server documentation for the UI framework you're using for more
specific instructions on what the `devServer` should be for that framework. Some
examples can be found in our
[framework documentation](/app/component-testing/component-framework-configuration).

:::

**Variant 1 (webpack & vite dev servers)**

<Badge type="danger">Before</Badge>

```js title="cypress/plugins/index.js"
const { startDevServer } = require('@cypress/webpack-dev-server')
const webpackConfig = require('../../webpack.config.js')

module.exports = (on, config) => {
  if (config.testingType === 'component') {
    on('dev-server:start', async (options) =>
      startDevServer({ options, webpackConfig })
    )
  }
}
```

<Badge type="success">After</Badge>

<Tabs>
<TabItem value="cypress.config.js" active>

```js
const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config.js')

module.exports = defineConfig({
  component: {
    devServer: {
      framework: 'react', // or vue
      bundler: 'webpack',
      webpackConfig,
    },
  },
})
```

</TabItem>
<TabItem value="cypress.config.js (verbose)">

```js
const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config.js')

module.exports = defineConfig({
  component: {
    devServer(cypressConfig) {
      return devServer({
        framework: 'react', // or vue
        cypressConfig,
        webpackConfig,
      })
    },
  },
})
```

</TabItem>
<TabItem value="cypress.config.ts">

```js
import { defineConfig } from 'cypress'
import webpackConfig from './webpack.config'

export default defineConfig({
  component: {
    devServer: {
      framework: 'react', // or vue
      bundler: 'webpack',
      webpackConfig,
    },
  },
})
```

</TabItem>
<TabItem value="cypress.config.ts (verbose)">

```js
import { defineConfig } from 'cypress'
import { devServer } from '@cypress/webpack-dev-server'
import webpackConfig from './webpack.config'

export default defineConfig({
  component: {
    devServer(cypressConfig) {
      return devServer({
        framework: 'react', // or vue
        cypressConfig,
        webpackConfig,
      })
    },
  },
})
```

</TabItem>
</Tabs>

**Variant 2 (react plugin dev servers)**

<Badge type="danger">Before</Badge>

```js
const devServer = require('@cypress/react/plugins/react-scripts')

module.exports = (on, config) => {
  if (config.testingType === 'component') {
    injectDevServer(on, config, {})
  }
}
```

<Badge type="success">After</Badge>

<Tabs>
<TabItem value="cypress.config.js" active>

```js
const { defineConfig } = require('cypress')

module.exports = defineConfig({
  component: {
    devServer: {
      framework: 'react', // or vue
      bundler: 'webpack',
    },
  },
})
```

</TabItem>
<TabItem value="more verbose">

```js
const { defineConfig } = require('cypress')
const webpackConfig = require('./webpack.config.js')

module.exports = defineConfig({
  component: {
    devServer(cypressConfig) {
      return devServer({
        framework: 'react', // or vue
        cypressConfig,
        webpackConfig,
      })
    },
  },
})
```

</TabItem>
</Tabs>

#### `experimentalStudio`

This option is no longer used. The experimental "Cypress Studio" has been
removed and will be rethought/revisited in a later release.

:::caution

Attempting to set the `experimentalStudio` config option will result in an error
when Cypress loads.

:::

#### `ignoreTestFiles` → `excludeSpecPattern`

The `ignoreTestFiles` option is no longer used, and has been replaced with the
`excludeSpecPattern`
[testing-type specific option](/app/references/configuration#Testing-Type-Specific-Options).

Default values

- `e2e.excludeSpecPattern` default value is `*.hot-update.js` (same as pervious
  ignore value)
- `component.excludeSpecPattern` default value is
  `['/snapshots/*', '/image_snapshots/*']` updated from `*.hot-update.js`
- The `**/node_modules/**` pattern is automatically added to both
  `e2e.specExcludePattern` and `component.specExcludePattern`, and does not need
  to be specified (and can't be overridden).

<Badge type="danger">Before</Badge>

```js
{
  "ignoreTestFiles": "path/to/**/*.js"
}
```

<Badge type="success">After</Badge>

```js
{
  component: {
    excludeSpecPattern: "path/to/**/*.js"
  },
  e2e: {
    excludeSpecPattern: "other/path/to/**/*.js"
  }
}
```

:::caution

Attempting to set the `ignoreTestFiles` config option will result in an error
when Cypress loads.

Also, attempting to set the `excludeSpecPattern` config option at the top level
of the configuration will result in an error when Cypress loads.

:::

#### `integrationFolder`

This option is no longer used, as it has been replaced by the `specPattern`
[testing-type specific option](/app/references/configuration#Testing-Type-Specific-Options).

:::caution

Attempting to set the `integrationFolder` config option will result in an error
when Cypress loads.

:::

#### `pluginsFile`

This option is no longer used, and all plugin file functionality has moved into
the [`setupNodeEvents()`](/app/references/configuration#setupNodeEvents) and
[`devServer`](/app/references/configuration#devServer) options. See the
[Plugins file removed](#Plugins-File-Removed) section of this migration guide
for more details.

:::caution

Attempting to set the `pluginsFile` config option will result in an error when
Cypress loads.

:::

#### `setupNodeEvents()`

All functionality related to setting up events or modifying the config,
previously done in the plugins file, has moved into the `setupNodeEvents()`
config options. This option is not valid at the top level of the config, and may
only be defined inside the `component` or `e2e`
[configuration objects](/app/references/configuration#Testing-Type-Specific-Options).

More information can be found in the
[Node Events Overview](/api/node-events/overview) and the
[Configuration API documentation](/api/node-events/configuration-api#Usage).

<Badge type="danger">Before</Badge> `cypress/plugins/index.js`

```js
module.exports = (on, config) => {
  if (config.testingType === 'component') {
    // component testing dev server setup code
    // component testing node events setup code
  } else {
    // e2e testing node events setup code
  }
}
```

<Badge type="success">After</Badge>

:::cypress-config-example

```ts
{
  component: {
    devServer(cypressConfig) {
      // component testing dev server setup code
    },
    setupNodeEvents(on, config) {
      // component testing node events setup code
    },
  },
  e2e: {
    setupNodeEvents(on, config) {
      // e2e testing node events setup code
    },
  },
}
```

:::

Alternately, you can continue to use an external plugins file, but you will need
to load that file explicitly, and also update it to move any component testing
dev server code into the [`devServer`](#devServer) config option.

:::cypress-config-example

```ts
import setupNodeEvents from './cypress/plugins/index.js'
```

```ts
{
  component: {
    devServer(cypressConfig) {
      // component testing dev server setup code
    },
    setupNodeEvents,
  },
  e2e: {
    setupNodeEvents,
  },
}
```

:::

#### `slowTestThreshold`

The `slowTestThreshold` configuration option is no longer valid at the top level
of the configuration, and is now a
[testing-type specific option](/app/references/configuration#Testing-Type-Specific-Options).

Note that the default values are unchanged (`10000` for `e2e` and `250` for
`component`).

:::caution

Attempting to set the `slowTestThreshold` config option at the top level of the
configuration will result in an error when Cypress loads.

:::

#### `supportFile`

The `supportFile` configuration option is no longer valid at the top level of
the configuration, and is now a
[testing-type specific option](/app/references/configuration#Testing-Type-Specific-Options).
More information can be found in the
[support file docs](/app/core-concepts/writing-and-organizing-tests#Support-file).

<Badge type="danger">Before</Badge>

```js
{
  "supportFile": "cypress/support/index.js"
}
```

<Badge type="success">After</Badge>

```js
{
  component: {
    supportFile: 'cypress/support/component.js'
  },
  e2e: {
    supportFile: 'cypress/support/e2e.js'
  }
}
```

:::caution

Attempting to set the `supportFile` config option at the top level of the
configuration will result in an error when Cypress loads.

Also, for a given testing type, multiple matching `supportFile` files will
result in an error when Cypress loads.

:::

#### `testFiles` → `specPattern`

The `testFiles` option is no longer used, and has been replaced with the
`specPattern` option, which must be defined inside the
[`component`](/app/references/configuration#component) and
[`e2e`](/app/references/configuration#e2e) configuration objects.

Default values:

- No longer matches with `.coffee` or `.cjsx`.
- `e2e.specPattern` default value is `cypress/e2e/**/*.cy.{js,jsx,ts,tsx}`.
- `component.specPattern` default value is `**/*.cy.{js,jsx,ts,tsx}`.

Important note about matching:

- E2E tests will be found using the `e2e.specPattern` value.
- Component tests will be found using the `component.specPattern` value but any
  tests found matching the `e2e.specPattern` value will be automatically
  excluded.

:::caution

Attempting to set the `testFiles` config option will result in an error when
Cypress loads.

Also, attempting to set the `specPattern` config option at the top level of the
configuration will result in an error when Cypress loads.

:::

### Updated Test File Locations

Previously, you could specify the locations of test files and folders using the
configuration options: `componentFolder`, or `integrationFolder`, and
`testFiles`. These options have been replaced with `specPattern`, which is not
valid at the top-level, but within the
[`component`](/app/references/configuration#component) or
[`e2e`](/app/references/configuration#e2e) configuration objects. For
example:

<Badge type="danger">Before</Badge>

```js
{
  "componentFolder": "src",
  "integrationFolder": "cypress/integration",
  "testFiles": "**/*.cy.js"
}
```

<Badge type="success">After</Badge>

```js
{
  component: {
    specPattern: 'src/**/*.cy.js'
  },
  e2e: {
    specPattern: 'cypress/integration/**/*.cy.js'
  }
}
```

:::caution

Attempting to set `componentFolder`, `integrationFolder`, or `testFiles` in the
config will result in an error when Cypress loads.

:::

:::danger

For Cypress Cloud users, changing your `specPattern` and files names or
extensions of your spec files will result in a loss of data in Cypress Cloud.
Because of this, if we detect your project is using Cypress Cloud during
automatic migration, we won't suggest changing your spec files. We also don't
recommend doing it manually if you are a Cypress Cloud user.

:::

### Generated Files

Generated screenshots and videos will still be created inside their respective
[folders](/app/references/configuration#Folders--Files) (`screenshotsFolder`,
`videosFolder`). However, the paths of generated files inside those folders will
be stripped of any common ancestor paths shared between all spec files found by
the `specPattern` option (or via the `--spec` command line option or `spec`
module API option, if specified).

Here are a few examples, assuming the value of `videosFolder` is
`cypress/videos`, `screenshotsFolder` is `cypress/screenshots` and
`cy.screenshot('my-screenshot')` is called once per spec file:

**Example 1**

- Spec file found
  - `cypress/e2e/path/to/file/one.cy.js`
- Common ancestor paths (calculated at runtime)
  - `cypress/e2e/path/to/file`
- Generated screenshot file
  - `cypress/screenshots/one.cy.js/my-screenshot.png`
- Generated video file
  - `cypress/videos/one.cy.js.mp4`

**Example 2**

- Spec files found
  - `cypress/e2e/path/to/file/one.cy.js`
  - `cypress/e2e/path/to/two.cy.js`
- Common ancestor paths (calculated at runtime)
  - `cypress/e2e/path/to`
- Generated screenshot files
  - `cypress/screenshots/file/one.cy.js/my-screenshot.png`
  - `cypress/screenshots/two.cy.js/my-screenshot.png`
- Generated video files
  - `cypress/videos/file/one.cy.js.mp4`
  - `cypress/videos/two.cy.js.mp4`

### Command / Cypress API Changes

#### `cy.mount()`

If you set up your app using the automatic configuration wizard, a basic
[`cy.mount()`](/api/commands/mount) command will be imported for you in your
support file from one our supported frameworks.

#### `Cypress.Commands.add()`

[`Cypress.Commands.add()`](/api/cypress-api/custom-commands) has been updated to
allow the built-in "placeholder" custom `mount` and `hover` commands to be
overwritten without needing to use `Cypress.Commands.overwrite()`.

### Component Testing Changes

Component Testing has moved from experimental to beta status in 10.0.0.

Component Testing can now be ran from the main app, and launching into component
testing via the command `cypress open-ct` is now deprecated. To launch directly
into component testing, use the `cypress open --component` command instead.

All the Component Testing dev servers are now included in the main `cypress` npm
package. Configuring them is done via specifying a framework and bundler in the
`devServer` config option, and the packages are no longer directly importable.
See
[Framework Configuration](/app/component-testing/component-framework-configuration)
for more info.

The mount libraries for React and Vue have also been included in the main
`cypress` package and can be imported from `cypress/react` and `cypress/vue`
respectively.

Any previous dev servers or mounting libraries from the `@cypress` namespace
should be uninstalled in Cypress 10.

### Clashing Types with Jest

You may want to consider configuring your app with a separate `tsconfig.json` to solve
[clashing types with jest](/app/tooling/typescript-support#Clashing-types-with-Jest).
You will need to exclude `cypress.config.ts`, `cypress`, `node_modules` in your
root `tsconfig.json` file.

```json
{
  "exclude": ["cypress.config.ts", "cypress", "node_modules"]
}
```

### Code Coverage Plugin

The [Cypress Code Coverage](https://github.com/cypress-io/code-coverage#readme)
plugin will need to be updated to version >= 3.10 to work with Cypress 10. Using
a previous version will result in an error when tests are ran with code coverage
enabled.

## Migrating from `cypress-file-upload` to [`.selectFile()`](/api/commands/selectfile)

Selecting files with input elements or dropping them over the page is available
in Cypress 9.3. Read the [`.selectFile()` API docs](/api/commands/selectfile)
for more information on how this works and how to use it. This guide details how
to change your test code to migrate from the
[`cypress-file-upload`](https://github.com/abramenal/cypress-file-upload) plugin
to `.selectFile()`.

### Quick guide

The argument signature is different for Cypress' builtin `.selectFile()` command
than the `.attachFile` command the `cypress-file-upload` plugin provided. You
can follow the steps below for each argument in order to migrate:

When the first argument is a file path:

- Prefix the path with `cypress/fixtures/`.

When the first argument is an object:

- `filePath`: Rename the property to `contents`. Prefix the value with
  `cypress/fixtures/`.
- `fileContent`: Rename the property to `contents`. Use
  [`Cypress.Buffer.from()`](/api/utilities/buffer) or other Buffer methods,
  rather than `Cypress.Blob`.
- `encoding`: Remove this property. It is no longer needed due to improved
  binary file handling in Cypress 9.0.
- `mimeType`: No change necessary. In most cases you do not need to give a
  mimeType explicitly. Cypress will attempt to infer the MIME type
  [based on the extension](https://github.com/jshttp/mime-types#mimelookuppath)
  of the fileName if none is provided.

In the second argument:

- `subjectType`: Rename this property to `action`. Change the value from
  `drag-n-drop` to `drag-drop` or from `input` to `select`.
- `allowEmpty`: Remove this property. `.selectFile()` does not check the length
  of a file read from disk, only its existence.
- `force`: Works the same with `.selectFile()` as it did in
  `cypress-file-upload`. No change necessary.

### Examples

Below are several examples of migrating various commands from
`cypress-file-upload` to the builtin `.selectFile()` command.

#### Read and attach a fixture

{/* prettier-ignore */}
<Badge type="danger">Before</Badge> Attaching a fixture from disk with `cypress-file-upload`

```js
cy.get('[data-cy="file-input"]').attachFile('myfixture.json')
```

{/* prettier-ignore */}
<Badge type="success">After</Badge> Selecting a fixture from disk with `.selectFile()`. Cypress follows paths from your project root (same as [`cy.readFile()`](/api/commands/readfile)).

```js
cy.get('[data-cy="file-input"]').selectFile('cypress/fixtures/myfixture.json')

// Or

cy.fixture('myfixture.json', { encoding: null }).as('myfixture')
cy.get('[data-cy="file-input"]').selectFile('@myfixture')
```

#### Using drag-n-drop

{/* prettier-ignore */}
<Badge type="danger">Before</Badge> Dragging and dropping a file with `cypress-file-upload`

```js
cy.get('[data-cy="dropzone"]').attachFile('myfixture.json', {
  subjectType: 'drag-n-drop',
})
```

{/* prettier-ignore */}
<Badge type="success">After</Badge> Selecting a fixture from disk with `.selectFile()`. Cypress follows paths from the root of your test folder (same as [`cy.readFile()`](/api/commands/readfile)).

```js
cy.get('[data-cy="dropzone"]').selectFile('fixtures/myfixture.json', {
  action: 'drag-drop',
})
```

#### Overriding the file name

{/* prettier-ignore */}
<Badge type="danger">Before</Badge> Dragging and dropping a file with `cypress-file-upload`

```js
cy.get('[data-cy="dropzone"]').attachFile({
  filePath: 'myfixture.json',
  fileName: 'customFileName.json',
})
```

{/* prettier-ignore */}
<Badge type="success">After</Badge> Selecting a fixture from disk with `.selectFile()`. Cypress follows paths from the root of your test folder (same as [`cy.readFile()`](/api/commands/readfile)).

```js
cy.get('[data-cy="dropzone"]').selectFile({
  contents: 'fixtures/myfixture.json',
  fileName: 'customFileName.json',
})
```

#### Working with file contents

{/* prettier-ignore */}
<Badge type="danger">Before</Badge> Working with file contents before using using `cypress-file-upload`

```js
const special = 'file.spss'

cy.fixture(special, 'binary')
  .then(Cypress.Blob.binaryStringToBlob)
  .then((fileContent) => {
    // ...process file contents
    cy.get('[data-cy="file-input"]').attachFile({
      fileContent,
      filePath: special,
      encoding: 'utf-8',
      lastModified: new Date().getTime(),
    })
  })
```

<Badge type="success">After</Badge> Working with file contents before using with
`.selectFile()`. The `null` encoding introduced in Cypress 9.0 makes working with
binary data simpler, and is the preferred encoding for use with `.selectFile()`.

```js
const special = 'file.spss'

cy.fixture(special, { encoding: null }).then((contents) => {
  // ...process file contents
  cy.get('[data-cy="file-input"]').selectFile({
    contents,
    fileName: special,
    lastModified: new Date().getTime(),
  })
})

// Or

cy.fixture(special, { encoding: null })
  .then((contents) => {
    // ...process file contents
  })
  .as('special')

cy.get('[data-cy="file-input"]').selectFile('@special')
```

#### Specifying a custom mimeType

{/* prettier-ignore */}
<Badge type="danger">Before</Badge> Specifying the MIME type with `cypress-file-upload`

```js
cy.get('[data-cy="dropzone"]').attachFile({
  filePath: 'myfixture.json',
  fileName: 'customFileName.json',
})
```

{/* prettier-ignore */}
<Badge type="success">After</Badge> Specifying a MIME type with `.selectFile()`.

```js
cy.get('[data-cy="dropzone"]').selectFile({
  contents: 'fixtures/myfixture.json',
  mimeType: 'text/plain',
})
```

## Migrating to Cypress 8.0

This guide details the changes and how to change your code to migrate to Cypress
8.0. [See the full changelog for 8.0](/app/references/changelog#8-0-0).

### `cypress run` runs all browsers `--headless`

When running `cypress run` previous to 8.0, some browsers would launch headed
while others were launched headless by default. In 8.0, we've normalized all
browsers to launch as headless by default.

This could cause a couple of changes to your existing runs:

- You may see the screenshot or video resolution of runs during `cypress run`
  change to the default of 1280x720. This is because headless browsers use the
  set screen size as opposed to the browser's size when opening headed.
- Chrome extensions will not load during a `--headless` run. If your run depends
  on a Chrome extension being loaded during `cypress run`, you should explicitly
  pass the `--headed` flag.

You can now remove the use of the `--headless` flag during `cypress run` as this
is the default for all browsers.

You should also update any use of the `isHeaded` or `isHeadless` property on
[`Cypress.browser`](/api/cypress-api/browser) or the
[browser launch API](/api/node-events/browser-launch-api) accordingly.

<Badge type="danger">Before</Badge> run headless browser

```script
cypress run --browser=chrome --headless
```

```script
cypress run --browser=firefox --headless
```

<Badge type="success">After</Badge> All browsers headless by default, so you can
remove the `--headless` flag during `cypress run`.

```script
cypress run --browser=chrome
```

```script
cypress run --browser=firefox
```

### Default screen size during `--headless`

The default screen size when running a headless browser has been reverted back
to 1280x720 pixels. If you have any code in the
[browser launch API](/api/node-events/browser-launch-api) to set the screen size to
1280x720, this can be removed.

<Badge type="danger">Before</Badge> set screen size to 1280x720

```js
// cypress/plugins/index.js
module.exports = (on, config) => {
  on('before:browser:launch', (browser, launchOptions) => {
    if (browser.name === 'chrome' && browser.isHeadless) {
      launchOptions.args.push('--window-size=1280,720')
    }

    if (browser.name === 'electron' && browser.isHeadless) {
      launchOptions.preferences.width = 1280
      launchOptions.preferences.height = 720
    }

    if (browser.name === 'firefox' && browser.isHeadless) {
      launchOptions.args.push('--width=1280')
      launchOptions.args.push('--height=720')
    }

    return launchOptions
  })
}
```

<Badge type="success">After</Badge> no longer need overrides

```js
// cypress/plugins/index.js
module.exports = (on, config) => {
  // the default screen size is 1280x720 in all headless browsers
}
```

## Migrating to Cypress 7.0

This guide details the changes and how to change your code to migrate to Cypress
7.0. [See the full changelog for 7.0](/app/references/changelog#7-0-0).

### [`cy.intercept()`][intercept] changes

[Cypress 7.0](/app/references/changelog#7-0-0) comes with some breaking
changes to [`cy.intercept()`][intercept]:

#### Handler ordering is reversed

Previous to Cypress 7.0, [`cy.intercept()`][intercept] handlers were run in the
order that they are defined, stopping after the first handler to call
`req.reply()`, or once all handlers are complete.

With Cypress 7.0, [`cy.intercept()`][intercept] handlers are now run in reverse
order of definition, stopping after the first handler to call `req.reply()`, or
once all handlers are complete.

This change was done so that users can override previously declared
[`cy.intercept()`][intercept] handlers by calling [`cy.intercept()`][intercept]
again. See [#9302](https://github.com/cypress-io/cypress/issues/9302) for more
details.

<Badge type="danger">Before</Badge>

```js
cy.intercept(url, (req) => {
  /* This will be called first! */
})
cy.intercept(url, (req) => {
  /* This will be called second! */
})
```

<Badge type="success">After</Badge>

```js
cy.intercept(url, (req) => {
  /* This will be called second! */
})
cy.intercept(url, (req) => {
  /* This will be called first! */
})
```

[Read more about the `cy.intercept()` interception lifecycle.](/api/commands/intercept#Interception-lifecycle)

#### URL matching is stricter

Before Cypress 7.0, [`cy.intercept()`][intercept] would match URLs against
strings by using `minimatch`, substring match, or by equality.

With Cypress 7.0, this behavior is being tightened - URLs are matched against
strings only by `minimatch` or by equality. The substring match has been
removed.

This more closely matches the URL matching behavior shown by `cy.route()`.
However, some intercepts will not match, even though they did before.

For example, requests with querystrings may no longer match:

```js
// will this intercept match a request for `/items?page=1`?
cy.intercept('/items')
// ✅ before 7.0.0, this will match, because it is a substring
// ❌ after 7.0.0, this will not match, because of the querystring
// solution: update the intercept to match the querystring with a wildcard:
cy.intercept('/items?*')
```

Also, requests for paths in nested directories may be affected:

```js
// will this intercept match a request for `/some/items`?
cy.intercept('/items')
// ✅ before 7.0.0, this will match, because it is a substring
// ❌ after 7.0.0, this will not match, because of the leading directory
// solution: update the intercept to include the directory:
cy.intercept('/some/items')
```

Additionally, the `matchUrlAgainstPath` `RouteMatcher` option that was added in
Cypress 6.2.0 has been removed in Cypress 7.0. It can be safely removed from
tests.

#### Deprecated `cy.route2()` command removed

`cy.route2()` was the original name for `cy.intercept()` during the experimental
phase of the feature. It was deprecated in Cypress 6.0. In Cypress 7.0, it has
been removed entirely. Please update existing usages of `cy.route2()` to call
`cy.intercept()` instead.

<Badge type="danger">Before</Badge>

```js
cy.route2('/widgets/*', { fixture: 'widget.json' }).as('widget')
```

<Badge type="success">After</Badge>

```js
cy.intercept('/widgets/*', { fixture: 'widget.json' }).as('widget')
```

#### `res.delay()` and `res.throttle()` have been renamed

The `res.delay()` and `res.throttle()` functions that exist on responses yielded
to response handlers have been renamed.

The new names are `res.setDelay()` and `res.setThrottle()`, respectively.

<Badge type="danger">Before</Badge>

```js
cy.intercept('/slow', (req) => {
  req.continue((res) => {
    // apply a delay of 1 second and a throttle of 56kbps
    res.delay(1000).throttle(56)
  })
})
```

<Badge type="success">After</Badge>

```js
cy.intercept('/slow', (req) => {
  req.continue((res) => {
    // apply a delay of 1 second and a throttle of 56kbps
    res.setDelay(1000).setThrottle(56)
  })
})
```

[Read more about available functions on `res`.](/api/commands/intercept#Intercepted-responses)

#### Falsy values are no longer dropped in `StaticResponse` bodies

Previously, falsy values supplied as the `body` of a `StaticResponse` would get
dropped (the same as if no body was supplied). Now, the bodies are properly
encoded in the response.

<Badge type="danger">Before</Badge>

```js
cy.intercept('/does-it-exist', { body: false })
// Requests to `/does-it-exist` receive an empty response body
```

<Badge type="success">After</Badge>

```js
cy.intercept('/does-it-exist', { body: false })
// Requests to `/does-it-exist` receive a response body of `false`
```

#### Errors thrown by request and response handlers are no longer wrapped

Previously, errors thrown inside of `req` and `res` handlers would be wrapped by
a `CypressError`. In 7.0.0, errors thrown inside of these handlers are not
wrapped before failing the test.

This should only affect users who are explicitly asserting on global errors. See
[#15189](https://github.com/cypress-io/cypress/issues/15189) for more details.

### Component Testing

In 7.0, component testing is no longer experimental. Cypress now ships with a
dedicated component test runner with a new UI and dedicated commands to launch
it.

**Changes are required for all existing projects**. The required changes are
limited to configuration and there are no breaking changes to the `mount` API.
The migration guide contains the following steps:

1. [Update your Cypress configuration to remove `experimentalComponentTesting`](#1-Remove-experimentalComponentTesting-config)
2. [Install updated dependencies](#2-Install-component-testing-dependencies)
3. [Update the plugins file](#3-Update-plugins-file-to-use-dev-serverstart)
4. [Use CLI commands to launch](#4-Use-CLI-commands-to-launch)
5. [Update the support file (optionally)](#5-Update-the-support-file-optionally)

#### 1. Remove `experimentalComponentTesting` config

The `experimentalComponentTesting` configuration is no longer needed to run
component tests. Remove this flag in order to run Cypress tests without
erroring.

<Badge type="danger">Before</Badge> experimentalComponentTesting flag is required
for component testing

```json
{
  "experimentalComponentTesting": true,
  "componentFolder": "src",
  "testFiles": "**/*spec.{js,jsx,ts,tsx}"
}
```

<Badge type="success">After</Badge> experimentalComponentTesting flag must be removed

```json
{
  "componentFolder": "src",
  "testFiles": "**/*spec.{js,jsx,ts,tsx}"
}
```

#### 2. Install component testing dependencies

The Component Test Runner requires the following dependencies:

- Framework-specific bindings such as
  [`@cypress/react`](https://www.npmjs.com/package/@cypress/react).
- Development server adapter such as
  [`@cypress/webpack-dev-server`](https://www.npmjs.com/package/@cypress/webpack-dev-server).
- Peer dependencies such as
  [`webpack-dev-server`](https://www.npmjs.com/package/webpack-dev-server),
  [`vue`][npmvue], or [`react`][npmreact].

**Install React dependencies**

1. Upgrade to [`@cypress/react`](https://www.npmjs.com/package/@cypress/react)
   5.X.
2. Install
   [`@cypress/webpack-dev-server`](https://www.npmjs.com/package/@cypress/webpack-dev-server).
3. (Optional) Install
   [`cypress-react-selector`](https://www.npmjs.com/package/cypress-react-selector)
   if any tests use `cy.react()`.
4. (Optional) Install code coverage, see
   [installation steps](/app/tooling/code-coverage)).

```shell
npm install cypress @cypress/react @cypress/webpack-dev-server --save-dev
```

**Install Vue 3 dependencies**

1. Upgrade to [`@cypress/vue@next`](https://www.npmjs.com/package/@cypress/vue)
   (3.X and above).
2. Install
   [`@cypress/webpack-dev-server`](https://www.npmjs.com/package/@cypress/webpack-dev-server).

```shell
npm install cypress @cypress/vue@next @cypress/webpack-dev-server --save-dev
```

**Install Vue 2 dependencies**

1. Upgrade to [`@cypress/vue@2`](https://www.npmjs.com/package/@cypress/vue)
   (2.X only).
2. Install
   [`@cypress/webpack-dev-server`](https://www.npmjs.com/package/@cypress/webpack-dev-server).

```shell
npm install cypress @cypress/vue @cypress/webpack-dev-server --save-dev
```

#### 3. Update plugins file to use `dev-server:start`

**Re-using a project's local development server instead of file preprocessors**

In 7.0 Cypress component tests require that code is bundled with your local
development server, via a new `dev-server:start` event. This event replaces the
previous `file:preprocessor` event.

<Badge type="danger">Before</Badge> Plugins file registers the file\:preprocessor
event

```js
const webpackPreprocessor = require('@cypress/webpack-preprocessor')
const webpackConfig = require('../webpack.config.js')

module.exports = (on, config) => {
  on('file:preprocessor', webpackPreprocessor(options))
}
```

<Badge type="success">After</Badge> Plugins file registers the dev-server\:start
event

```js
// The @cypress/webpack-dev-server package replaces @cypress/webpack-preprocessor
const { startDevServer } = require('@cypress/webpack-dev-server')
const webpackConfig = require('../webpack.config.js')

module.exports = (on, config) => {
  // You must use the dev-server:start event instead of the file:preprocessor event

  on('dev-server:start', (options) => {
    return startDevServer({ options, webpackConfig })
  })
}
```

**Configure `plugins.js` for React projects**

Projects using React may not need to update their plugins file. If your project
is using a webpack scaffold or boilerplate, it is recommended to use a preset
plugin imported from
[`@cypress/react/plugins`](https://github.com/cypress-io/cypress/tree/v7.7.0/npm/react/plugins).

**Preset Plugins for React**

If you are using a preset plugin within
[`@cypress/react`](https://www.npmjs.com/package/@cypress/react), you should not
need to update your plugins file. To check if you are using a preset, check to
see if your plugins file contains an import to a file inside of
`@cypress/react/plugins`.

<Badge type="success">After</Badge> An example plugins file to configure component
testing in a React Scripts project

```js
// The @cypress/react project exposes preset plugin configurations
// These presets automatically register the events to bundle the project properly
const injectReactScriptsDevServer = require('@cypress/react/plugins/react-scripts')

module.exports = (on, config) => {
  // Internally, this method registers `dev-server:start` with the proper webpack configuration
  // Previously, it registered the `file:preprocessor` event.
  injectReactScriptsDevServer(on, config)

  return config
}
```

**Configure `plugins.js` for Vue**

Projects using Vue will likely be using either
[`@vue/cli`](https://cli.vuejs.org/) or manually defining webpack configuration.
These steps are identical to the manual setup steps, with the exception of how
you resolve the webpack configuration. To access the resolved webpack
configuration that contains any `vue.config.js` setup or the default
[`@vue/cli`](https://cli.vuejs.org/) webpack setup, you must import the
configuration and pass it into
[`@cypress/webpack-dev-server`](https://www.npmjs.com/package/@cypress/webpack-dev-server).

<Badge type="success">After</Badge> An example plugins file to configure component
testing in a Vue CLI project

```js
const { startDevServer } = require('@cypress/webpack-dev-server')

// The resolved configuration, which contains any `vue.config.js` setup
const webpackConfig = require('@vue/cli-service/webpack.config.js')

module.exports = (on, config) => {
  on('dev-server:start', (options) => {
    return startDevServer({ options, webpackConfig })
  })
}
```

**Configuring a project with vanilla webpack**

For projects with manually defined or ejected webpack configurations, the
webpack configuration must be passed in.

<Badge type="success">After</Badge> An example plugins file to configure component
testing in a project with vanilla webpack

```js
const { startDevServer } = require('@cypress/webpack-dev-server')
const webpackConfig = require('../webpack.config.js')

module.exports = (on, config) => {
  on('dev-server:start', (options) => {
    return startDevServer({ options, webpackConfig })
  })
}
```

#### 4. Use CLI commands to launch

To run your component tests you _must_ use the dedicated component testing
subcommands.

- `cypress open-ct`
- `cypress run-ct`

:::info

Component tests will no longer be picked up when launching Cypress from
`cypress open` or `cypress run`. Please use `cypress open-ct` or
`cypress run-ct`.

:::

<Badge type="danger">Before</Badge> Commands launches both end-to-end and component
tests.

<CypressRunCommands />

<Badge type="success">After</Badge> Command launches Cypress Component Test Runner
and executes component tests. End-to-end tests are run separately.

```shell
# open component testing runner
cypress open-ct

# run all component tests
cypress run-ct

# e2e tests
cypress open
cypress run
```

#### 5. Update the support file (optionally)

Previously, a support file was required to set up the component testing target
node. This is no longer necessary.

Specifically for React users, if the support file contains the following line,
please remove it. The import will fail in the future. We have left it in to
avoid a breaking change, but the file does nothing.

<Badge type="danger">Before</Badge> The support file was required to import a script
from <a href="https://www.npmjs.com/package/@cypress/react">@cypress/react</a>

```js
// support.js

// This import should be removed, it will error in a future update
import '@cypress/react/hooks'
```

#### Expanded stylesheet support

Stylesheets are now bundled and imported within spec and support files.
Previously, many of `mount`'s mounting options such as `stylesheets`,
`cssFiles`, and `styles` were required to import stylesheets into your component
tests. This often involved pre-compiling the stylesheets before launching the
component tests, which affected performance. Migrating to imports for these
styles is optional, but recommended.

Now, stylesheets should be loaded into the `document` the same way they are in
your application. It is recommended you update your code like so:

<Badge type="danger">Before</Badge> Stylesheets were loaded using the filesystem

```js
const { mount } = require('@cypress/react')
const Button = require('./Button')

it('renders a Button', () => {
  // Mounting a button and loading the Tailwind CSS library
  mount(<Button />, {
    stylesheets: [
      // Paths are relative to the project root directory and must be pre-compiled
      // Because they are static, they do not watch for file updates
      '/dist/index.css',
      '/node_modules/tailwindcss/dist/tailwind.min.css',
    ],
  })
})
```

<Badge type="success">After</Badge> Stylesheets are supported via an import and `mountingOptions.stylesheets`
is not recommended

```js
// In the majority of modern style-loaders,
// these styles will be injected into document.head when they're imported below
require('./index.scss')
require('tailwindcss/dist/tailwind.min.css')

const { mount } = require('@cypress/react')
const Button = require('./Button')

it('renders a Button', () => {
  // This button will render with the Tailwind CSS styles
  // as well as the application's index.scss styles
  mount(<Button />)
})
```

#### Desktop GUI no longer displays component tests

Previously, the Desktop GUI displayed _both_ end-to-end and component tests.
Now, component tests are only displayed when launching via the component
testing-specific subcommands. `cypress open-ct` (or `run-ct` in CI)

#### Executing all or some component tests

In 6.X, the Desktop GUI had support for finding and executing a subset of
component tests. In 7.0, this is possible with the `--headed` command and a spec
glob, like so:

```sh
cypress run-ct --headed --spec **/some-folder/*spec.*
```

#### Coverage

Previously, the [`@cypress/react`](https://www.npmjs.com/package/@cypress/vue)
4.X package embedded code coverage in your tests automatically.

If you still wish to record code coverage in your tests, you must manually
install it. Please see our [code coverage guide](/app/tooling/code-coverage)
for the latest steps.

#### cypress-react-selector

If you use `cy.react()` in your tests, you must manually install
[`cypress-react-selector`](https://www.npmjs.com/package/cypress-react-selector)
with `npm install cypress-react-selector --save-dev`. You do not need to update your support
file.

#### HTML Side effects

As of 7.0, we only clean up components mounted by Cypress via
[`@cypress/react`](https://www.npmjs.com/package/@cypress/react) or
[`@cypress/vue`](https://www.npmjs.com/package/@cypress/vue).

We no longer automatically reset the `document.body` between tests. Any HTML
side effects of your component tests will carry over.

<Badge type="danger">Before</Badge> All HTML content was cleared between spec files

```jsx
const { mount } = require('@cypress/react')

describe('Component teardown behavior', () => {
  it('modifies the document and mounts a component', () => {
    // HTML unrelated to the component is mounted
    Cypress.$('body').append('<div data-cy="some-html"/>')

    // A component is mounted
    mount(<Button data-cy="my-button"></Button>)

    cy.get('[data-cy="some-html"]').should('exist')
    cy.get('[data-cy="my-button"]').should('exist')
  })

  it('cleans up any HTML', () => {
    // The component is automatically unmounted by Cypress
    cy.get('[data-cy="my-button"]').should('not.exist')

    // The HTML left over from the previous test has been cleaned up
    // This was done automatically by Cypress
    cy.get('[data-cy="some-html"]').should('not.exist')
  })
})
```

<Badge type="success">After</Badge> Only the components are cleaned up between spec
files

```jsx
const { mount } = require('@cypress/react')

describe('Component teardown behavior', () => {
  it('modifies the document and mounts a component', () => {
    // HTML unrelated to the component is mounted
    Cypress.$('body').append('<div data-cy="some-html"/>')

    // A component is mounted
    mount(<Button data-cy="my-button"></Button>)

    cy.get('[data-cy="some-html"]').should('exist')
    cy.get('[data-cy="my-button"]').should('exist')
  })

  it('only cleans up *components* between tests', () => {
    // The component is automatically unmounted by Cypress
    cy.get('[data-cy="my-button"]').should('not.exist')

    // The HTML left over from the previous test should be manually cleared
    cy.get('[data-cy="some-html"]').should('not.exist')
  })
})
```

#### Legacy `cypress-react-unit-test` and `cypress-vue-unit-test` packages

For users upgrading from
[`cypress-react-unit-tests`](https://www.npmjs.com/package/cypress-react-unit-test)
or
[`cypress-vue-unit-tests`](https://www.npmjs.com/package/cypress-vue-unit-test),
please update all references to use
[`@cypress/react`](https://www.npmjs.com/package/@cypress/react) or
[`@cypress/vue`](https://www.npmjs.com/package/@cypress/vue). These packages
have been deprecated and moved to the Cypress scope on npm.

### Uncaught exception and unhandled rejections

In 7.0, Cypress now fails tests in more situations where there is an uncaught
exception and also if there is an unhandled promise rejection in the application
under test.

You can ignore these situations and not fail the Cypress test with the code
below.

#### Turn off all uncaught exception handling

```javascript
Cypress.on('uncaught:exception', (err, runnable) => {
  // returning false here prevents Cypress from
  // failing the test
  return false
})
```

#### Turn off uncaught exception handling unhandled promise rejections

```javascript
Cypress.on('uncaught:exception', (err, runnable, promise) => {
  // when the exception originated from an unhandled promise
  // rejection, the promise is provided as a third argument
  // you can turn off failing the test in this case
  if (promise) {
    // returning false here prevents Cypress from
    // failing the test
    return false
  }
})
```

### Node.js 12+ support

Cypress comes bundled with its own
[Node.js version](https://github.com/cypress-io/cypress/blob/develop/.node-version).
However, installing the `cypress` npm package uses the Node.js version installed
on your system.

Node.js 10 reached its end of life on Dec 31, 2019 and Node.js 13 reached its
end of life on June 1, 2019.
[See Node's release schedule](https://github.com/nodejs/Release). These Node.js
versions will no longer be supported when installing Cypress. The minimum
Node.js version supported to install Cypress is Node.js 12 or Node.js 14+.

## Migrating `cy.route()` to [`cy.intercept()`][intercept]

This guide details how to change your test code to migrate from `cy.route()` to
[`cy.intercept()`][intercept]. `cy.server()` and `cy.route()` are deprecated in
Cypress 6.0.0. In a future release, support for `cy.server()` and `cy.route()`
will be removed.

Please also refer to the full documentation for [cy.intercept()][intercept].

### Match simple route

In many use cases, you can replace `cy.route()` with [cy.intercept()][intercept]
and remove the call to `cy.server()` (which is no longer necessary).

<Badge type="danger">Before</Badge>

```js
// Set up XHR listeners using cy.route()
cy.server()
cy.route('/users').as('getUsers')
cy.route('POST', '/project').as('createProject')
cy.route('PATCH', '/projects/*').as('updateProject')
```

<Badge type="success">After</Badge>

```js
// Intercept HTTP requests
cy.intercept('/users').as('getUsers')
cy.intercept('POST', '/project').as('createProject')
cy.intercept('PATCH', '/projects/*').as('updateProject')
```

### Match against `url` and `path`

The `url` argument to [cy.intercept()][intercept] matches against the full url,
as opposed to the `url` or `path` in `cy.route()`. If you're using the `url`
argument in [`cy.intercept()`][intercept], you may need to update your code
depending on the route you're trying to match.

<Badge type="danger">Before</Badge>

```js
// Match XHRs with a path or url of /users
cy.server()
cy.route({
  method: 'POST',
  url: '/users',
}).as('getUsers')
```

<Badge type="success">After</Badge>

```js
// Match HTTP requests with a path of /users
cy.intercept({
  method: 'POST',
  path: '/users',
}).as('getUsers')

// OR
// Match HTTP requests with an exact url of https://example.cypress.io/users
cy.intercept({
  method: 'POST',
  url: 'https://example.cypress.io/users',
}).as('getUsers')
```

### `cy.wait()` object

The object returned by `cy.wait()` is different from intercepted HTTP requests
using [`cy.intercept()`][intercept] than the object returned from an awaited
`cy.route()` XHR.

<Badge type="danger">Before</Badge>

```js
// Wait for XHR from cy.route()
cy.route('POST', '/users').as('createUser')
// ...
cy.wait('@createUser').then(({ requestBody, responseBody, status }) => {
  expect(status).to.eq(200)
  expect(requestBody.firstName).to.eq('Jane')
  expect(responseBody.firstName).to.eq('Jane')
})
```

<Badge type="success">After</Badge>

```js
// Wait for intercepted HTTP request
cy.intercept('POST', '/users').as('createUser')
// ...
cy.wait('@createUser').then(({ request, response }) => {
  expect(response.statusCode).to.eq(200)
  expect(request.body.name).to.eq('Jane')
  expect(response.body.name).to.eq('Jane')
})
```

### Fixtures

You can stub requests and response with fixture data by defining a `fixture`
property in the `routeHandler` argument for [`cy.intercept()`][intercept].

<Badge type="danger">Before</Badge>

```js
// Stub response with fixture data using cy.route()
cy.route('GET', '/projects', 'fx:projects')
```

<Badge type="success">After</Badge>

```js
// Stub response with fixture data using cy.intercept()
cy.intercept('GET', '/projects', {
  fixture: 'projects',
})
```

### Override intercepts

As of 7.0, newer intercepts are called before older intercepts, allowing users
to override intercepts.
[See "Handler ordering is reversed" for more details](#Handler-ordering-is-reversed).

Before 7.0, intercepts could not be overridden. See
[#9302](https://github.com/cypress-io/cypress/issues/9302) for more details.

## Migrating to Cypress 6.0

This guide details the changes and how to change your code to migrate to Cypress
6.0. [See the full changelog for 6.0](/app/references/changelog#6-0-0).

### Non-existent element assertions

:::info

**Key takeway:** Use `.should('not.exist')` to assert that an element does not
exist in the DOM (not `.should('not.be.visible')`, etc).

:::

In previous versions of Cypress, there was a possibility for tests to falsely
pass when asserting a negative state on non-existent elements.

For example, in the tests below we want to test that the search dropdown is no
longer visible when the search input is blurred because we hide the element in
CSS styles. Except in this test, we've mistakenly misspelled one of our
selectors.

```js
cy.get('input[type=search]').type('Cypress')
cy.get('#dropdown').should('be.visible')
cy.get('input[type=search]').blur()

// below we misspelled "dropdown" in the selector 😞
// the assertions falsely pass in Cypress < 6.0
// and will correctly fail in Cypress 6.0 +
cy.get('#dropdon').should('not.be.visible')
cy.get('#dropdon').should('not.have.class', 'open')
cy.get('#dropdon').should('not.contain', 'Cypress')
```

<DocsImage
  src="/img/app/migration-guide/el-incorrectly-passes-existence-check.png"
  alt="non-existent element before 6.0"
/>

In 6.0, these assertions will now correctly fail, telling us that the `#dropdon`
element doesn't exist in the DOM.

<DocsImage
  src="/img/app/migration-guide/el-correctly-fails-existence-check.png"
  alt="non-existent element in 6.0"
/>

#### Assertions on non-existent elements

This fix may cause some breaking changes in your tests if you are relying on
assertions such as `not.be.visible` or `not.contains` to test that the DOM
element did not _exist_ in the DOM. This means you'll need to update your test
code to be more specific about your assertions on non-existent elements.

<Badge type="danger">Before</Badge> Assert that non existent element was not visible

```js
it('test', () => {
  // the modal element is removed from the DOM on click
  cy.get('[data-cy="modal"]').find('.close').click()
  // assertions below pass in Cypress < 6.0, but properly fail in 6.0+
  cy.get('[data-cy="modal"]').should('not.be.visible')
  cy.get('[data-cy="modal"]').should('not.contain', 'Upgrade')
})
```

<Badge type="success">After</Badge> Assert that non existent element does not exist

```js
it('test', () => {
  // the modal element is removed from the DOM on click
  cy.get('data-cy="modal"').find('.close').click()
  // we should instead assert that the element doesn't exist
  cy.get('data-cy="modal"').should('not.exist')
})
```

### Opacity visibility

DOM elements with `opacity: 0` style are no longer considered to be visible.
This includes elements with an ancestor that has `opacity: 0` since a child
element can never have a computed opacity greater than that of an ancestor.

Elements where the CSS property (or ancestors) is `opacity: 0` are still
considered [actionable](/app/core-concepts/interacting-with-elements) however
and
[any action commands](/app/core-concepts/interacting-with-elements#Actionability)
used to interact with the element will perform the action. This matches
browser's implementation on how they regard elements with `opacity: 0`.

#### Assert visibility of `opacity: 0` element

<Badge type="danger">Before</Badge> Failed assertion that `opacity: 0` element is
not visible.

```js
it('test', () => {
  // '.hidden' has 'opacity: 0' style.
  // In < 5.0 this assertion would fail
  cy.get('.hidden').should('not.be.visible')
})
```

<Badge type="success">After</Badge> Passed assertion that `opacity: 0` element is
not visible.

```js
it('test', () => {
  // '.hidden' has 'opacity: 0' style.
  // In 6.0 this assertion will pass
  cy.get('.hidden').should('not.be.visible')
})
```

#### Perform actions on `opacity: 0` element

In all versions of Cypress, you can interact with elements that have
`opacity: 0` style.

```js
it('test', () => {
  // '.hidden' has 'opacity: 0' style.
  cy.get('.hidden').click() // ✅ clicks on element
  cy.get('.hidden').type('hi') // ✅ types into element
  cy.get('.hidden').check() // ✅ checks element
  cy.get('.hidden').select('yes') // ✅ selects element
})
```

### `cy.wait(alias)` type

`cy.route()` is deprecated in 6.0.0. We encourage the use of
[cy.intercept()][intercept] instead. Due to this deprecation, the type yielded
by [cy.wait(alias)](/api/commands/wait) has changed.

<Badge type="danger">Before</Badge> Before 6.0.0, [cy.wait(alias)](/api/commands/wait)
would yield an object of type `WaitXHR`.

<Badge type="success">After</Badge> In 6.0.0 and onwards, [cy.wait(alias)](/api/commands/wait)
will yield an object of type `Interception`. This matches the new interception object
type used for [cy.intercept()][intercept].

#### Restore old behavior

If you need to restore the type behavior prior to 6.0.0 for
[cy.wait(alias)](/api/commands/wait), you can declare a global override for
[cy.wait()](/api/commands/wait) like so:

```ts
declare global {
  namespace Cypress {
    interface Chainable<Subject = any> {
      wait(alias: string): Chainable<Cypress.WaitXHR>
    }
  }
}
```

### `—disable-dev-shm-usage`

We now pass `—disable-dev-shm-usage` to the Chrome browser flags by default. If
you're passing this flag in your `plugins` file, you can now remove this code.

<Badge type="danger">Before</Badge> Passing flag in plugins file.

```js
// cypress/plugins/index.js
module.exports = (on, config) => {
  on('before:browser:launch', (browser = {}, launchOptions) => {
    if (browser.family === 'chromium' && browser.name !== 'electron') {
      launchOptions.args.push('--disable-dev-shm-usage')
    }

    return launchOptions
  })
}
```

<Badge type="success">After</Badge> Remove flag from plugins file.

```js
// cypress/plugins/index.js
module.exports = (on, config) => {}
```

#### Restore old behavior

If you need to remove the flag in 6.0.0+, you can follow the workaround
documented here: [#9242](https://github.com/cypress-io/cypress/issues/9242).

## Migrating to Cypress 5.0

This guide details the changes and how to change your code to migrate to Cypress
5.0. [See the full changelog for 5.0](/app/references/changelog#5-0-0).

### Tests retries

Test retries are available in Cypress 5.0. This means that tests can be re-run a
number of times before potentially being marked as a failed test. Read the
[Test Retries](/app/guides/test-retries) doc for more information on how this
works and how to turn on test retries.

When test retries are turned on, there will now be a screenshot taken for every
failed attempt, so there could potentially be more than 1 screenshot per test
failure. Read the [Test Retries](/app/guides/test-retries) doc for more
information on how this works.

The
[`cypress-plugin-retries`](https://github.com/Bkucera/cypress-plugin-retries)
plugin has been deprecated in favor of test retries built into Cypress. There's
guidance below on how to migrate from the
[`cypress-plugin-retries`](https://github.com/Bkucera/cypress-plugin-retries)
plugin to Cypress's built-in test retries.

#### Configure test retries via the CLI

<Badge type="danger">Before</Badge> Setting retries with `cypress-plugin-retries`
via env vars

```shell
CYPRESS_RETRIES=2 cypress run
```

<Badge type="success">After</Badge> Setting test retries in Cypress 5.0 via env vars

```shell
CYPRESS_RETRIES=2 cypress run
```

#### Configure test retries in the configuration file

<Badge type="danger">Before</Badge> Setting retries with `cypress-plugin-retries`
via configuration

```json
{
  "env": {
    "RETRIES": 2
  }
}
```

<Badge type="success">After</Badge> Setting test retries in Cypress 5.0 via configuration

```json
{
  "retries": 1
}
```

- `runMode` allows you to define the number of test retries when running
  `cypress run`
- `openMode` allows you to define the number of test retries when running
  `cypress open`

```json
{
  "retries": {
    "runMode": 1,
    "openMode": 3
  }
}
```

#### Configure test retries per test

<Badge type="danger">Before</Badge> Setting retries with `cypress-plugin-retries`
via the test

```js
it('test', () => {
  Cypress.currentTest.retries(2)
})
```

<Badge type="success">After</Badge> Setting test retries in Cypress 5.0 via test
options

```js
it(
  'allows user to login',
  {
    retries: 2,
  },
  () => {
    // ...
  }
)
```

- `runMode` allows you to define the number of test retries when running
  `cypress run`
- `openMode` allows you to define the number of test retries when running
  `cypress open`

```js
it(
  'allows user to login',
  {
    retries: {
      runMode: 2,
      openMode: 3,
    },
  },
  () => {
    // ...
  }
)
```

### Module API results

To more accurately reflect result data for runs with
[test retries](/app/guides/test-retries), the structure of each run's `runs`
array resolved from the `Promise` returned from `cypress.run()` of the Module
API has changed.

Mainly there is a new `attempts` Array on each `test` which will reflect the
result of each test retry.

<Badge type="danger">Before</Badge> `results.runs` Module API results

```json
{
  // ...
  "runs": [{
    // ...
    "hooks": [{
      "hookId": "h1",
      "hookName": "before each",
      "title": [ "before each hook" ],
      "body": "function () {\n  expect(true).to.be["true"];\n}"
    }],
    // ...
    "screenshots": [{
      "screenshotId": "8ddmk",
      "name": null,
      "testId": "r2",
      "takenAt": "2020-08-05T08:52:20.432Z",
      "path": "User/janelane/my-app/cypress/screenshots/spec.js/test (failed).png",
      "height": 720,
      "width": 1280
    }],
    "stats": {
      // ...
      "wallClockStartedAt": "2020-08-05T08:38:37.589Z",
      "wallClockEndedAt": "2018-07-11T17:53:35.675Z",
      "wallClockDuration": 1171
    },
    "tests": [{
      "testId": "r2",
      "title": [ "test" ],
      "state": "failed",
      "body": "function () {\n  expect(true).to.be["false"];\n}",
      "stack": "AssertionError: expected true to be false\n' +
        '    at Context.eval (...cypress/integration/spec.js:5:21",
      "error": "expected true to be false",
      "timings": {
        "lifecycle": 16,
        "test": {...}
      },
      "failedFromHookId": null,
      "wallClockStartedAt": "2020-08-05T08:38:37.589Z",
      "wallClockDuration": 1171,
      "videoTimestamp": 4486
    }],
  }],
  // ...
}
```

<Badge type="success">After</Badge> `results.runs` Module API results

```json
{
  // ...
  "runs": [{
    // ...
    "hooks": [{
      "hookName": "before each",
      "title": [ "before each hook" ],
      "body": "function () {\n  expect(true).to.be["true"];\n}"
    }],
    // ...
    "stats": {
      // ...
      "startedAt": "2020-08-05T08:38:37.589Z",
      "endedAt": "2018-07-11T17:53:35.675Z",
      "duration": 1171
    },
    "tests": [{
      "title": [ "test" ],
      "state": "failed",
      "body": "function () {\n  expect(true).to.be["false"];\n}",
      "displayError": "AssertionError: expected true to be false\n' +
      '    at Context.eval (...cypress/integration/spec.js:5:21",
      "attempts": [{
        "state": "failed",
        "error": {
          "message": "expected true to be false",
          "name": "AssertionError",
          "stack": "AssertionError: expected true to be false\n' +
      '    at Context.eval (...cypress/integration/spec.js:5:21"
        },
        "screenshots": [{
          "name": null,
          "takenAt": "2020-08-05T08:52:20.432Z",
          "path": "User/janelane/my-app/cypress/screenshots/spec.js/test (failed).png",
          "height": 720,
          "width": 1280
        }],
        "startedAt": "2020-08-05T08:38:37.589Z",
        "duration": 1171,
        "videoTimestamp": 4486
      }]
    }],
  }],
  // ...
}
```

### Cookies `whitelist` option renamed

The [Cypress.Cookies.defaults()](/api/cypress-api/cookies) `whitelist` option
has been renamed to `preserve` to more closely reflect its behavior.

<Badge type="danger">Before</Badge> `whitelist` option

```js
Cypress.Cookies.defaults({
  whitelist: 'session_id',
})
```

<Badge type="success">After</Badge> `preserve` option

```js
Cypress.Cookies.defaults({
  preserve: 'session_id',
})
```

### `blacklistHosts` configuration renamed

The `blacklistHosts` configuration has been renamed to
[blockHosts](/app/references/configuration#Notes) to more closely reflect its
behavior.

This should be updated in all places where Cypress configuration can be set
including via the Cypress configuration file, command line arguments, the
`pluginsFile`, `Cypress.config()` or environment variables.

<Badge type="danger">Before</Badge> `blacklistHosts` configuration

```json
{
  "blacklistHosts": "www.google-analytics.com"
}
```

<Badge type="success">After</Badge> `blockHosts` configuration

```json
{
  "blockHosts": "www.google-analytics.com"
}
```

### Return type of `Cypress.Blob` changed

We updated the [Blob](https://github.com/nolanlawson/blob-util) library used
behind [Cypress.Blob](/api/utilities/blob) from `1.3.3` to `2.0.2`.

The return type of the [Cypress.Blob](/api/utilities/blob) methods
`arrayBufferToBlob`, `base64StringToBlob`, `binaryStringToBlob`, and
`dataURLToBlob` have changed from `Promise<Blob>` to `Blob`.

<Badge type="danger">Before</Badge> `Cypress.Blob` methods returned a Promise

```js
Cypress.Blob.base64StringToBlob(this.logo, 'image/png').then((blob) => {
  // work with the returned blob
})
```

<Badge type="success">After</Badge> `Cypress.Blob` methods return a Blob

```js
const blob = Cypress.Blob.base64StringToBlob(this.logo, 'image/png')

// work with the returned blob
```

### `cy.server()` `whitelist` option renamed

The `cy.server()` `whitelist` option has been renamed to `ignore` to more
closely reflect its behavior.

<Badge type="danger">Before</Badge> `whitelist` option

```js
cy.server({
  whitelist: (xhr) => {
    return xhr.method === 'GET' && /\.(jsx?|html|css)(\?.*)?$/.test(xhr.url)
  },
})
```

<Badge type="success">After</Badge> `ignore` option

```js
cy.server({
  ignore: (xhr) => {
    return xhr.method === 'GET' && /\.(jsx?|html|css)(\?.*)?$/.test(xhr.url)
  },
})
```

### Cookies `sameSite` property

Values yielded by [cy.setCookie()](/api/commands/setcookie),
[cy.getCookie()](/api/commands/getcookie), and
[cy.getCookies()](/api/commands/getcookies) will now contain the `sameSite`
property if specified.

If you were using the `experimentalGetCookiesSameSite` configuration to get the
`sameSite` property previously, this should be removed.

<Badge type="danger">Before</Badge> Cookies yielded before had no `sameSite` property.

```js
cy.getCookie('token').then((cookie) => {
  // cy.getCookie() yields a cookie object
  // {
  //   domain: "localhost",
  //   expiry: 1593551644,
  //   httpOnly: false,
  //   name: "token",
  //   path: "/commands",
  //   secure: false,
  //   value: "123ABC"
  // }
})
```

<Badge type="success">After</Badge> Cookies yielded now have `sameSite` property
if specified.

```js
cy.getCookie('token').then((cookie) => {
  // cy.getCookie() yields a cookie object
  // {
  //   domain: "localhost",
  //   expiry: 1593551644,
  //   httpOnly: false,
  //   name: "token",
  //   path: "/commands",
  //   sameSite: "strict",
  //   secure: false,
  //   value: "123ABC"
  // }
})
```

### dirname / filename

The globals `__dirname` and `__filename` no longer include a leading slash.

{/* prettier-ignore */}
<Badge type="danger">Before</Badge> `__dirname` / `__filename`

```js
// cypress/integration/app_spec.js
it('include leading slash < 5.0', () => {
  expect(__dirname).to.equal('/cypress/integration')
  expect(__filename).to.equal('/cypress/integration/app_spec.js')
})
```

{/* prettier-ignore */}
<Badge type="success">After</Badge> `__dirname` / `__filename`

```js
// cypress/integration/app_spec.js
it('do not include leading slash >= 5.0', () => {
  expect(__dirname).to.equal('cypress/integration')
  expect(__filename).to.equal('cypress/integration/app_spec.js')
})
```

### Linux dependencies

Running Cypress on Linux now requires the `libgbm` dependency (on Debian-based
systems, this is available as `libgbm-dev`). To install all required
dependencies on Ubuntu/Debian, you can run the script below:

```shell
apt-get install libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libnss3 libxss1 libasound2 libxtst6 xauth xvfb
```

### TypeScript esModuleInterop

Cypress no longer forces the `esModuleInterop` compiler option for TypeScript to
be `true` for spec, support, and plugins files. We recommend setting it in your
project's `tsconfig.json` instead if you need to.

```json
// tsconfig.json
{
  "compilerOptions": {
    "esModuleInterop": true
    /* ... other compiler options ... */
  }
}
```

### TypeScript 3.4+ support

Cypress 5.0 raises minimum required TypeScript version from 2.9+ to 3.4+. You'll
need to have TypeScript 3.4+ installed within your project to have TypeScript
support within Cypress.

### Node.js 10+ support

Cypress comes bundled with its own
[Node.js version](https://github.com/cypress-io/cypress/blob/develop/.node-version).
However, installing the `cypress` npm package uses the Node.js version installed
on your system.

Node.js 8 reached its end of life on Dec 31, 2019 and Node.js 11 reached its end
of life on June 1, 2019.
[See Node's release schedule](https://github.com/nodejs/Release). These Node.js
versions will no longer be supported when installing Cypress. The minimum
Node.js version supported to install Cypress is Node.js 10 or Node.js 12+.

## Migrating to Cypress 4.0

This guide details the changes and how to change your code to migrate to Cypress
4.0. [See the full changelog for 4.0](/app/references/changelog#4-0-0).

### Mocha upgrade

Mocha was upgraded from `2.5.3` to `7.0.1`, which includes a number of breaking
changes and new features outlined in their
[changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md). Some
changes you might notice are described below.

#### <Icon name="exclamation-triangle" color="red" /> Breaking Change: invoke `done` callback and return a promise

Starting with
[Mocha 3.0.0](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md#300--2016-07-31),
invoking a `done` callback _and_ returning a promise in a test results in an
error.

This error originates from Mocha and is discussed at length
[here](https://github.com/mochajs/mocha/pull/1320) and
[here](https://github.com/mochajs/mocha/issues/2407).

The reason is that using two different ways to signal that a test is finished is
usually a mistake and there is always a way to only use one. There is a
[proposal to handle this situation without erroring](https://github.com/mochajs/mocha/issues/2509)
that may be released in a future version of Mocha.

In the meantime, you can fix the error by choosing a single way to signal the
end of your test's execution.

##### Example #1

<Badge type="danger">Before</Badge> This test has a done callback and a promise

```javascript
it('uses invokes done and returns promise', (done) => {
  return codeUnderTest.doSomethingThatReturnsPromise().then((result) => {
    // assertions here
    done()
  })
})
```

<Badge type="success">After</Badge> You can remove the `done` callback and return
the promise instead:

```javascript
it('uses invokes done and returns promise', () => {
  return codeUnderTest.doSomethingThatReturnsPromise().then((result) => {
    // assertions here
  })
})
```

##### Example #2

<Badge type="danger">Before</Badge> Sometimes it might make more sense to use the
`done` callback and not return a promise:

```javascript
it('uses invokes done and returns promise', (done) => {
  eventEmitter.on('change', () => {
    // assertions
    done()
  })

  return eventEmitter.doSomethingThatEmitsChange()
})
```

<Badge type="success">After</Badge> In this case, you don't need to return the promise:

```javascript
it('uses invokes done and returns promise', (done) => {
  eventEmitter.on('change', () => {
    // assertions
    done()
  })

  eventEmitter.doSomethingThatEmitsChange()
})
```

##### Example #3

Test functions using `async/await` automatically return a promise, so they need
to be refactored to not use a `done` callback.

<Badge type="danger">Before</Badge> This will cause an overspecified error.

```javascript
it('uses async/await', async (done) => {
  const eventEmitter = await getEventEmitter()
  eventEmitter.on('change', () => done())
  eventEmitter.doSomethingThatEmitsChange()
})
```

<Badge type="success">After</Badge> Update to the test code below.

```javascript
it('uses async/await', async () => {
  const eventEmitter = await getEventEmitter()
  return new Promise((resolve) => {
    eventEmitter.on('change', () => resolve())
    eventEmitter.doSomethingThatEmitsChange()
  })
})
```

#### <Icon name="exclamation-triangle" color="red" /> Tests require a title

Tests now require a title and will error when not provided one.

```javascript
// Would show as pending in Cypress 3
// Will throw type error in Cypress 4:
it() // Test argument "title" should be a string. Received type "undefined"
```

### Chai upgrade

Chai was upgraded from `3.5.0` to `4.2.0`, which includes a number of breaking
changes and new features outlined in
[Chai's migration guide](https://github.com/chaijs/chai/issues/781). Some
changes you might notice are described below.

#### <Icon name="exclamation-triangle" color="red" /> Breaking Change: assertions expecting numbers

Some assertions will now throw an error if the assertion's target or arguments
are not numbers, including `within`, `above`, `least`, `below`, `most`,
`increase` and `decrease`.

```javascript
// These will now throw errors:
expect(null).to.be.within(0, 1)
expect(null).to.be.above(10)
// This will not throw errors:
expect('string').to.have.a.length.of.at.least(3)
```

#### <Icon name="exclamation-triangle" color="red" /> Breaking Change: `empty` assertions

The `.empty` assertion will now throw when it is passed non-string primitives
and functions.

```javascript
// These will now throw TypeErrors
expect(Symbol()).to.be.empty
expect(() => {}).to.be.empty
```

#### <Icon name="exclamation-triangle" color="red" /> Breaking Change: non-existent properties

An error will throw when a non-existent property is read. If there are typos in
property assertions, they will now appear as failures.

```javascript
// Would pass in Cypress 3 but will fail correctly in 4
expect(true).to.be.ture
```

#### <Icon name="exclamation-triangle" color="red" /> Breaking Change: `include` checks strict equality

`include` now always use strict equality unless the `deep` property is set.

<Badge type="danger">Before</Badge> `include` would always use deep equality

```javascript
// Would pass in Cypress 3 but will fail correctly in 4
cy.wrap([
  {
    first: 'Jane',
    last: 'Lane',
  },
]).should('include', {
  first: 'Jane',
  last: 'Lane',
})
```

<Badge type="success">After</Badge> Need to specificy `deep.include` for deep equality

```javascript
// Specifically check for deep.include to pass in Cypress 4
cy.wrap([
  {
    first: 'Jane',
    last: 'Lane',
  },
]).should('deep.include', {
  first: 'Jane',
  last: 'Lane',
})
```

### Sinon.JS upgrade

Sinon.JS was upgraded from `3.2.0` to `8.1.1`, which includes a number of
breaking changes and new features outlined in
[Sinon.JS's migration guide](https://sinonjs.org/releases/latest/#migration-guides).
Some changes you might notice are described below.

#### <Icon name="exclamation-triangle" color="red" /> Breaking Change: stub non-existent properties

An error will throw when trying to stub a non-existent property.

```javascript
// Would pass in Cypress 3 but will fail in 4
cy.stub(obj, 'nonExistingProperty')
```

#### <Icon name="exclamation-triangle" color="red" /> Breaking Change: `reset()` replaced by `resetHistory()`

For spies and stubs, the `reset()` method was replaced by `resetHistory()`.

<Badge type="danger">Before</Badge> Spies and stubs using `reset()`.

```javascript
const spy = cy.spy()
const stub = cy.stub()

spy.reset()
stub.reset()
```

{/* prettier-ignore */}
<Badge type="success">After</Badge> Update spies and stubs should now use `resetHistory()`.

```javascript
const spy = cy.spy()
const stub = cy.stub()

spy.resetHistory()
stub.resetHistory()
```

### Plugin Event `before:browser:launch`

Since we now support more advanced browser launch options, during
`before:browser:launch` we no longer yield the second argument as an array of
browser arguments and instead yield a `launchOptions` object with an `args`
property.

You can see more examples of the new `launchOptions` in use in the
[Browser Launch API doc](/api/node-events/browser-launch-api).

<Badge type="danger">Before</Badge> The second argument is no longer an array.

```js
on('before:browser:launch', (browser, args) => {
  // will print a deprecation warning telling you
  // to change your code to the new signature
  args.push('--another-arg')

  return args
})
```

{/* prettier-ignore */}
<Badge type="success">After</Badge> Access the `args` property off `launchOptions`

```js
on('before:browser:launch', (browser, launchOptions) => {
  launchOptions.args.push('--another-arg')

  return launchOptions
})
```

### Electron options in `before:browser:launch`

Previously, you could pass options to the launched Electron
[BrowserWindow](https://www.electronjs.org/docs/api/browser-window#new-browserwindowoptions)
in `before:browser:launch` by modifying the `launchOptions` object.

Now, you must pass those options as `launchOptions.preferences`:

<Badge type="danger">Before</Badge> Passing BrowserWindow options on the `launchOptions`
object is no longer supported.

```js
on('before:browser:launch', (browser, args) => {
  args.darkTheme = true

  return args
})
```

<Badge type="success">After</Badge> Pass BrowserWindow options on the `options.preferences`
object instead.

```js
on('before:browser:launch', (browser, launchOptions) => {
  launchOptions.preferences.darkTheme = true

  return launchOptions
})
```

### Launching Chrome Canary with `--browser`

Before 4.0, `cypress run --browser canary` would run tests in Chrome Canary.

Now, you must pass `--browser chrome:canary` to select Chrome Canary.

See the
[docs for `cypress run --browser`](/app/references/command-line#cypress-run-browser-lt-browser-name-or-path-gt)
for more information.

<Badge type="danger">Before</Badge> Passing `canary` will no longer find a browser

```shell
cypress run --browser canary
```

<Badge type="success">After</Badge> Pass `chrome:canary` to launch Chrome Canary

```shell
cypress run --browser chrome:canary
```

### Chromium-based browser `family`

We updated the [Cypress browser objects](/api/node-events/browser-launch-api) of all
Chromium-based browsers, including Electron, to have `chromium` set as their
`family` field.

```js
module.exports = (on, config) => {
  on('before:browser:launch', (browser = {}, launchOptions) => {
    if (browser.family === 'electron') {
      // would match Electron in 3.x
      // will match no browsers in 4.0.0
      return launchOptions
    }

    if (browser.family === 'chromium') {
      // would match no browsers in 3.x
      // will match any Chromium-based browser in 4.0.0
      // ie Chrome, Canary, Chromium, Electron, Edge (Chromium-based)
      return launchOptions
    }
  })
}
```

#### Example #1 (Finding Electron)

<Badge type="danger">Before</Badge> This will no longer find the Electron browser.

```js
module.exports = (on, config) => {
  on('before:browser:launch', (browser = {}, args) => {
    if (browser.family === 'electron') {
      // run code for Electron browser in 3.x
      return args
    }
  })
}
```

<Badge type="success">After</Badge> Use `browser.name` to check for Electron

```js
module.exports = (on, config) => {
  on('before:browser:launch', (browser = {}, launchOptions) => {
    if (browser.name === 'electron') {
      // run code for Electron browser in 4.0.0
      return launchOptions
    }
  })
}
```

#### Example #2 (Finding Chromium-based browsers)

<Badge type="danger">Before</Badge> This will no longer find any browsers.

```js
module.exports = (on, config) => {
  on('before:browser:launch', (browser = {}, args) => {
    if (browser.family === 'chrome') {
      // in 4.x, `family` was changed to 'chromium' for all Chromium-based browsers
      return args
    }
  })
}
```

<Badge type="success">After</Badge> Use `browser.name` and `browser.family` to select
non-Electron Chromium-based browsers

```js
module.exports = (on, config) => {
  on('before:browser:launch', (browser = {}, launchOptions) => {
    if (browser.family === 'chromium' && browser.name !== 'electron') {
      // pass launchOptions to Chromium-based browsers in 4.0
      return launchOptions
    }
  })
}
```

### `cy.writeFile()` yields `null`

`cy.writeFile()` now yields `null` instead of the `contents` written to the
file. This change was made to more closely align with the behavior of Node.js
[`fs.writeFile`](https://nodejs.org/api/fs.html#fs_fs_writefile_file_data_options_callback).

<Badge type="danger">Before</Badge> This assertion will no longer pass

```js
cy.writeFile('path/to/message.txt', 'Hello World').then((text) => {
  // Would pass in Cypress 3 but will fail in 4
  expect(text).to.equal('Hello World') // false
})
```

<Badge type="success">After</Badge> Instead read the contents of the file

```js
cy.writeFile('path/to/message.txt', 'Hello World')
cy.readFile('path/to/message.txt').then((text) => {
  expect(text).to.equal('Hello World') // true
})
```

### cy.contains() ignores invisible whitespaces

Browsers ignore leading, trailing, duplicate whitespaces. And Cypress now does
that, too.

```html
<p>hello world</p>
```

```javascript
cy.get('p').contains('hello world') // Fail in 3.x. Pass in 4.0.0.
cy.get('p').contains('hello\nworld') // Pass in 3.x. Fail in 4.0.0.
```

### Node.js 8+ support

Cypress comes bundled with its own
[Node.js version](https://github.com/cypress-io/cypress/blob/develop/.node-version).
However, installing the `cypress` npm package uses the Node.js version installed
on your system.

Node.js 4 reached its end of life on April 30, 2018 and Node.js 6 reached its
end of life on April 30, 2019.
[See Node's release schedule](https://github.com/nodejs/Release). These Node.js
versions will no longer be supported when installing Cypress. The minimum
Node.js version supported to install Cypress is Node.js 8.

### CJSX is no longer supported

Cypress no longer supports CJSX (CoffeeScript + JSX), because the library used
to transpile it is no longer maintained.

If you need CJSX support, you can use a pre-2.x version of the Browserify
preprocessor.

```shell
npm install @cypress/browserify-preprocessor@1.1.2
```

```javascript
// cypress/plugins/index.js
const browserify = require('@cypress/browserify-preprocessor')

module.exports = (on) => {
  on('file:preprocessor', browserify())
}
```

[intercept]: /api/commands/intercept
[npmcypresswebpackdevserver]: https://www.npmjs.org/package/@cypress/webpack-dev-server
[npmcypressreact]: https://www.npmjs.org/package/@cypress/react
[npmcypressvue]: https://www.npmjs.org/package/@cypress/vue
[npmreact]: https://www.npmjs.org/package/react
[npmvue]: https://www.npmjs.org/package/vue
[npmwebpackdevserver]: https://www.npmjs.org/package/webpack-dev-server
